├── .gitignore ├── Gemfile ├── README.markdown ├── Rakefile ├── lib ├── seo_helper.rb └── seo_helper │ ├── configuration.rb │ ├── engine.rb │ ├── engine3.rb │ ├── format.rb │ ├── helper.rb │ ├── railtie.rb │ └── version.rb └── seo_helper.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | # Specify your gem's dependencies in seo_helper.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # SeoHelper 2 | 3 | SeoHelper is a set of helpers for rendering page-specific `` tag, common `<meta />` tags and `<link rel='image_src' />` tag. 4 | 5 | It contains not only a set of `tag()` helper wrappers, but can also help you doing things like 6 | 7 | * specifying **title**, **description** and **image_url** for the current page, and render them with `<title>` and corresponding `meta`-tags or `link`-tags 8 | * automatically append **page number** and **site name** into the title (both formats are customizable) 9 | 10 | Pull requests are welcome. 11 | 12 | # Usage 13 | 14 | Add this to your Gemfile: 15 | 16 | gem 'seo_helper', '~> 1.0' 17 | 18 | If you want to use the edge version on Github, specify the `:git` option. 19 | 20 | gem 'seo_helper', '~> 1.0', :git => 'git://github.com/techbang/seo_helper.git' 21 | 22 | And run 23 | 24 | bundle install 25 | 26 | to install this plug-in. 27 | 28 | After installation, add `config/initializers/seo_helper.rb` into your Rails application, specifying the basic configuration like this: 29 | 30 | ```ruby 31 | SeoHelper.configure do |config| 32 | config.site_name = "My Awesome Web Application" 33 | end 34 | ``` 35 | 36 | Please specify at least the `site_name`, or SeoHelper will take the class name of your Rails application as default. 37 | 38 | Don't forget to restart your Rails application. 39 | 40 | # Page Title Rendering 41 | 42 | It's always been a complex job to render a pretty title and put it in both the `<title>` tag and the title `meta` tag. With SeoHelper, you can easily set and render the title text. 43 | 44 | ## Rendering the Default Title in Layout 45 | 46 | First, put this in your layout file, between `<head>` and `</head>`: 47 | 48 | ```ruby 49 | <%= render_page_title_tag %> 50 | ``` 51 | 52 | You can replace the hard-coded `<title>` tag with this helper. 53 | 54 | Now visit any page of your application, you'll see that the browser's title will be the `site_name` that you specified (or the application's class name if not specified). 55 | 56 | ## Specifying the Title 57 | 58 | To specify the title of current page in an controller action, for example, in the `Articles#show` action, use `set_page_title` 59 | 60 | ```ruby 61 | def show 62 | # ... 63 | set_page_title @article.title 64 | end 65 | ``` 66 | 67 | Now visit any article, you'll see the title becomes 68 | 69 | Article Title | My Awesome Web Application 70 | 71 | The `site_name` is still a part of the title text. 72 | 73 | If `params[:page]` exists it'll automatically be appended to the title. For example, in `Articles#index`, when you visit the 2nd page, you'll see the title becomes 74 | 75 | Article Index - Page 2 | My Awesome Web Application 76 | 77 | # Rendering SEO-oriented Tags 78 | 79 | For SEO, you may want to put some `<meta>` tags and `<link rel="image-src" />` tag in your webpage. With SeoHelper, you can easily do these things. 80 | 81 | ## Rendering Meta Tags for Title, Description and Keywords 82 | 83 | First, put this in your layout file, between `<head>` and `</head>`: 84 | 85 | ```erb 86 | <%= render_page_title_meta_tag %><%# This might be unnecessary %> 87 | <%= render_page_description_meta_tag %> 88 | <%= render_page_keywords_meta_tag %> 89 | ``` 90 | 91 | You can replace the hard-coded `<meta>` tags with these helpers. 92 | 93 | To specify the description and keywords of current page in an controller action, for example, in the `Articles#show` action, use `set_page_description` 94 | 95 | ```ruby 96 | def show 97 | # ... 98 | set_page_title @article.title # same as <title> tag 99 | set_page_description @article.excerpt # or @article.content.truncate(100) 100 | set_page_keywords @article.tags # or @article.keywords 101 | end 102 | ``` 103 | 104 | Now visit any article, and open it's source, you'll see these meta tags: 105 | 106 | ```html 107 | <meta name="title" content="Article Title | My Awesome Web Application" /> 108 | <meta name="description" content="Lorem ipsum dolor sit amet, ..." /> 109 | <meta name="keywords" content="faketext,lorem,ipsum" /> 110 | ``` 111 | 112 | If `set_pages_keywords` receives an array, it'll convert the array to a string joined with a single comma (`','`). 113 | 114 | ## Rendering `<link rel="image_src" />` tag 115 | 116 | This tag tells search engine what the related image of this page is. To render this tag, put `render_page_image_link_tag` in your layout file, between `<head>` and `</head>`: 117 | 118 | ```ruby 119 | <%= render_page_image_link_tag %> 120 | ``` 121 | 122 | You can replace the hard-coded `<link rel="image_src" />` tag with this helper. 123 | 124 | To specify the title of current page in an controller action, for example, in the `Articles#show` action, use `set_page_image` 125 | 126 | ```ruby 127 | def show 128 | # ... 129 | set_page_image @article.excerpt_image_url 130 | end 131 | ``` 132 | 133 | Now visit any article, and open it's source, you'll see this link tag: 134 | 135 | ```html 136 | <link href="http://www.example.com/articles/123/the-excerpt-image.jpg" rel="image_src" /> 137 | ``` 138 | 139 | ## Re-use Page-Specific SEO Data in View 140 | 141 | Sometimes you may want to use the page title, description, keywords and the image url. Say you're working with [OpenGraph Meta tags](http://developers.facebook.com/docs/opengraph/), you may re-use them in your view: 142 | 143 | ```erb 144 | <meta property="og:title" content="<%= page_title %>"/> 145 | <meta property="og:description" content="<%= page_description %>"/> 146 | <meta property="og:image" content="<%= page_image %>"/> 147 | ``` 148 | 149 | You may also want to use our [OpenGraphHelper](https://github.com/techbang/open_graph_helper), too. 150 | 151 | ## Blank Tags: Skipping Them or Not 152 | 153 | Sometimes you just want to skip SEO tags when their contents are empty. This is the default behavior of SeoHelper, that is, if you've never `set_page_description` to something in a request flow, the meta description tag will not be rendered. 154 | 155 | If you want to force SeoHelper to render a default description meta tag, keywords meta tag, or image_src link tag, you can explicitly tell SeoHelper with configure block, in the initializer: 156 | 157 | ```ruby 158 | SeoHelper.configure do |config| 159 | # ... 160 | config.skip_blank = false 161 | config.default_page_description = "This is a really really awesome website" 162 | config.default_page_keywords = "hello,world,yay" 163 | config.default_page_image = "http://www.example.com/title.png" 164 | end 165 | ``` 166 | 167 | Then, when page description, keywords or image was not specified, SeoHelper will fallback to default text. 168 | 169 | ## Rendering SEO-oriented Tags Directly 170 | 171 | While SeoHelper provides `set_page_XXX` and `render_page_XXX_meta_tag` helper pairs, it is still possible to render these meta tags directly, with the corresponding `XXX_meta_tag` helpers, the `title_tag` helper and the `image_src_link_tag` helper. 172 | 173 | For example, 174 | 175 | ```erb 176 | <%= title_tag("Hello! World") %> 177 | <%= description_meta_tag("Lorem ipsum dolor sit amet!") %> 178 | <%= image_src_link_tag("http://www.example.com/title.png") %> 179 | ``` 180 | 181 | yields 182 | 183 | ```html 184 | <title>Hello! World 185 | 186 | 187 | ``` 188 | 189 | You can also add Site Name to the customized title, just put `true` as the second argument. For example, if you set `site_name` to `My Awesome Web Application`, then 190 | 191 | ```erb 192 | <%= title_tag("Hello! World", true) %> 193 | ``` 194 | 195 | yields 196 | 197 | ```html 198 | Hello! World | My Awesome Web Application 199 | ``` 200 | 201 | which applies the default format of site name in page title, and thus customizable (see below). 202 | 203 | # Customizing Page Title Format 204 | 205 | The page number format and site name format in a page title are customizable, via the configure block. They're basically lambda blocks, and invoked dynamically during runtime. 206 | 207 | ## Customizing Format of Page Number in Title 208 | 209 | The default format of Page Number in Title is: 210 | 211 | Title - Page X | My Awesome Web Application 212 | 213 | Changing the page number format by specifying a new lambda block: 214 | 215 | ```ruby 216 | SeoHelper.configure do |config| 217 | config.pagination_formatter = lambda { |title, page_number| "#{title} - Page No.#{page_number}" } 218 | end 219 | ``` 220 | 221 | Now you'll get 222 | 223 | Title - Page No.X | My Awesome Web Application 224 | 225 | as your page title. 226 | 227 | ## Customizing Format of Site Name in TItle 228 | 229 | The default format of Site Name in Title is: 230 | 231 | Title | My Awesome Web Application 232 | 233 | Changing the site name format by specifying a new lambda block: 234 | 235 | ```ruby 236 | SeoHelper.configure do |config| 237 | config.site_name_formatter = lambda { |title, site_name| "#{title} « #{site_name}" } 238 | end 239 | ``` 240 | 241 | Now you'll get 242 | 243 | Title « My Awesome Web Application 244 | 245 | as your page title. 246 | 247 | # License 248 | 249 | This software is released under the MIT License. 250 | 251 | Copyright (c) 2011- Techbang 252 | 253 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 254 | 255 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 256 | 257 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 258 | 259 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | -------------------------------------------------------------------------------- /lib/seo_helper.rb: -------------------------------------------------------------------------------- 1 | require 'seo_helper/configuration' 2 | require 'seo_helper/format' 3 | 4 | module SeoHelper 5 | class << self 6 | attr_accessor :configuration 7 | end 8 | 9 | module Rails 10 | case ::Rails.version.to_s 11 | when /^4/ 12 | require "seo_helper/engine" 13 | when /^3\.[12]/ 14 | require "seo_helper/engine3" 15 | when /^3\.[0]/ 16 | require "seo_helper/railtie" 17 | end 18 | 19 | end 20 | 21 | def self.configure 22 | self.configuration ||= Configuration.new 23 | yield(configuration) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/seo_helper/configuration.rb: -------------------------------------------------------------------------------- 1 | module SeoHelper 2 | class Configuration 3 | attr_accessor :site_name 4 | 5 | attr_accessor :default_page_description 6 | attr_accessor :default_page_keywords 7 | attr_accessor :default_page_image 8 | 9 | attr_accessor :skip_blank 10 | 11 | attr_accessor :pagination_formatter 12 | attr_accessor :site_name_formatter 13 | 14 | def initialize 15 | # Set default site_name according to the Rails application class name 16 | self.site_name = ::Rails.application.class.to_s.split("::").first 17 | self.skip_blank = true 18 | 19 | self.default_page_description = "" 20 | self.default_page_keywords = "" 21 | self.default_page_image = "" 22 | 23 | self.pagination_formatter = lambda {|title, page_number| "#{title} - Page #{page_number}" } 24 | self.site_name_formatter = lambda {|text, site_name| "#{text} | #{site_name}"} 25 | end 26 | 27 | end 28 | end -------------------------------------------------------------------------------- /lib/seo_helper/engine.rb: -------------------------------------------------------------------------------- 1 | require "seo_helper/helper" 2 | 3 | module SeoHelper 4 | class Engine < ::Rails::Engine 5 | initializer "seo_helper.view_helpers" do 6 | ActionView::Base.send :include, SeoHelper::Helper 7 | end 8 | 9 | initializer "seo_helper.controller_helpers" do 10 | ActionController::Base.send :include, SeoHelper::ControllerHelper 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/seo_helper/engine3.rb: -------------------------------------------------------------------------------- 1 | require "seo_helper/helper" 2 | 3 | module SeoHelper 4 | class Engine3 < ::Rails::Engine 5 | initializer "seo_helper.view_helpers" do 6 | ActionView::Base.send :include, SeoHelper::Helper 7 | end 8 | 9 | initializer "seo_helper.controller_helpers" do 10 | ActionController::Base.send :include, SeoHelper::ControllerHelper 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/seo_helper/format.rb: -------------------------------------------------------------------------------- 1 | module SeoHelper 2 | def self.format_current_page(title, page_number) 3 | self.configuration.pagination_formatter.call(title, page_number) 4 | end 5 | 6 | def self.format_site_name(text, site_name) 7 | self.configuration.site_name_formatter.call(text, site_name) 8 | end 9 | end -------------------------------------------------------------------------------- /lib/seo_helper/helper.rb: -------------------------------------------------------------------------------- 1 | module SeoHelper 2 | module Helper 3 | 4 | # ... 5 | def title_tag(title, with_site_name=false) 6 | title = SeoHelper.format_site_name(title, SeoHelper.configuration.site_name) if with_site_name == true 7 | content_tag(:title, h(title), nil, false) # option=nil, escape=false 8 | end 9 | 10 | # 11 | def title_meta_tag(title, with_site_name=false) 12 | title = SeoHelper.format_site_name(title, SeoHelper.configuration.site_name) if with_site_name == true 13 | tag(:meta, {:name => "title", :content => h(title)}) 14 | end 15 | 16 | # 17 | def description_meta_tag(content, with_site_name=false) 18 | title = SeoHelper.format_site_name(title, SeoHelper.configuration.site_name) if with_site_name == true 19 | # use String.new to avoid always (html_save == true) which makes h() inactive 20 | content = h(String.new(strip_tags(content))) 21 | tag(:meta, { :name => "description", :content => content }) 22 | end 23 | 24 | # 25 | def keywords_meta_tag(keywords) 26 | keywords = keywords.join(',') if keywords.is_a? Array 27 | tag(:meta, {:name => "keywords", :content => h(keywords)}) 28 | end 29 | 30 | # 31 | def image_src_link_tag(image_url) 32 | tag(:link, { :rel => "image_src", :href => image_url }) 33 | end 34 | 35 | # 36 | def robots_meta_tag(content = "INDEX,FOLLOW") 37 | tag(:meta, { :name => "robots", :content => content }) 38 | end 39 | 40 | attr_reader :page_title, :page_description, :page_keywords, :page_image 41 | 42 | def render_page_title_tag 43 | # fallback to site_name if @page_title has never been set 44 | title_tag(page_title || SeoHelper.configuration.site_name) 45 | end 46 | 47 | def render_page_title_meta_tag 48 | title_meta_tag(page_title || SeoHelper.configuration.site_name) 49 | end 50 | 51 | # @page_xxx | skip_blank | don't skip_blank 52 | # ----------+---------------+------------------- 53 | # present | @page_xxx | @page_xxx 54 | # blank | (no output) | default_page_xxx 55 | 56 | def render_page_description_meta_tag 57 | return if SeoHelper.configuration.skip_blank && page_description.blank? 58 | description_meta_tag(page_description || SeoHelper.configuration.default_page_description) 59 | end 60 | 61 | def render_page_keywords_meta_tag 62 | return if SeoHelper.configuration.skip_blank && page_keywords.blank? 63 | keywords_meta_tag(page_keywords || SeoHelper.configuration.default_page_keywords) 64 | end 65 | 66 | def render_page_image_link_tag 67 | return if SeoHelper.configuration.skip_blank && page_image.blank? 68 | image_src_link_tag(page_image || SeoHelper.configuration.default_page_image) 69 | end 70 | end 71 | 72 | module ControllerHelper 73 | 74 | # will also append current page number and the site name 75 | def set_page_title(title, site_name = nil) 76 | site_name ||= SeoHelper.configuration.site_name 77 | 78 | if params[:page] 79 | @page_title = SeoHelper.format_current_page(title, params[:page]) 80 | else 81 | @page_title = title 82 | end 83 | @page_title = SeoHelper.format_site_name(@page_title, site_name) 84 | end 85 | 86 | def set_page_description(description) 87 | @page_description = description 88 | end 89 | 90 | def set_page_keywords(keywords) 91 | @page_keywords = keywords 92 | end 93 | 94 | def set_page_image(image_src) 95 | @page_image = image_src 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /lib/seo_helper/railtie.rb: -------------------------------------------------------------------------------- 1 | require "seo_helper/helper" 2 | 3 | module SeoHelper 4 | class Railtie < Rails::Railtie 5 | initializer "seo_helper.view_helpers" do 6 | ActionView::Base.send :include, SeoHelper::Helper 7 | end 8 | 9 | initializer "seo_helper.controller_helpers" do 10 | ActionController::Base.send :include, SeoHelper::ControllerHelper 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/seo_helper/version.rb: -------------------------------------------------------------------------------- 1 | module SeoHelper 2 | VERSION = "1.0.2" 3 | end 4 | -------------------------------------------------------------------------------- /seo_helper.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/seo_helper/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["xdite"] 6 | gem.email = ["xdite@techbang.com.tw"] 7 | gem.description = %q{SEO helper of Techbang} 8 | gem.summary = %q{SEO helper of Techbang} 9 | gem.homepage = "" 10 | 11 | gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 12 | gem.files = `git ls-files`.split("\n") 13 | gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 14 | gem.name = "seo_helper" 15 | gem.require_paths = ["lib"] 16 | gem.version = SeoHelper::VERSION 17 | end 18 | --------------------------------------------------------------------------------