meta_tags-rails 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +3 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +21 -0
  7. data/CHANGELOG.md +135 -0
  8. data/Gemfile +13 -0
  9. data/MIT-LICENSE +22 -0
  10. data/README.md +627 -0
  11. data/Rakefile +28 -0
  12. data/lib/meta_tags-rails.rb +37 -0
  13. data/lib/meta_tags-rails/configuration.rb +21 -0
  14. data/lib/meta_tags-rails/content_tag.rb +14 -0
  15. data/lib/meta_tags-rails/controller_helper.rb +44 -0
  16. data/lib/meta_tags-rails/meta_tags_collection.rb +176 -0
  17. data/lib/meta_tags-rails/renderer.rb +253 -0
  18. data/lib/meta_tags-rails/tag.rb +25 -0
  19. data/lib/meta_tags-rails/text_normalizer.rb +148 -0
  20. data/lib/meta_tags-rails/version.rb +4 -0
  21. data/lib/meta_tags-rails/view_helper.rb +205 -0
  22. data/meta-tags.gemspec +30 -0
  23. data/spec/configuration_spec.rb +14 -0
  24. data/spec/controller_helper_spec.rb +42 -0
  25. data/spec/spec_helper.rb +84 -0
  26. data/spec/text_normalizer/normalize_title_spec.rb +43 -0
  27. data/spec/text_normalizer/truncate_array_spec.rb +60 -0
  28. data/spec/view_helper/charset_spec.rb +16 -0
  29. data/spec/view_helper/custom_spec.rb +67 -0
  30. data/spec/view_helper/description_spec.rb +61 -0
  31. data/spec/view_helper/icon_spec.rb +42 -0
  32. data/spec/view_helper/keywords_spec.rb +58 -0
  33. data/spec/view_helper/links_spec.rb +125 -0
  34. data/spec/view_helper/module_spec.rb +41 -0
  35. data/spec/view_helper/noindex_spec.rb +107 -0
  36. data/spec/view_helper/open_graph_spec.rb +86 -0
  37. data/spec/view_helper/open_search_spec.rb +33 -0
  38. data/spec/view_helper/refresh_spec.rb +32 -0
  39. data/spec/view_helper/title_spec.rb +155 -0
  40. data/spec/view_helper/twitter_spec.rb +31 -0
  41. data/spec/view_helper_spec.rb +57 -0
  42. metadata +172 -0
@@ -0,0 +1,25 @@
1
+ module MetaTags
2
+ # Represents an HTML meta tag with no content (<tag />).
3
+ class Tag
4
+ attr_reader :name, :attributes
5
+
6
+ # Initializes a new instance of Tag class.
7
+ #
8
+ # @param [String, Symbol] name HTML tag name
9
+ # @param [Hash] attributes list of HTML tag attributes
10
+ #
11
+ def initialize(name, attributes = {})
12
+ @name = name
13
+ @attributes = attributes
14
+ end
15
+
16
+ # Render tag into a Rails view.
17
+ #
18
+ # @param [ActionView::Base] view instance of a Rails view.
19
+ # @return [String] HTML string for the tag.
20
+ #
21
+ def render(view)
22
+ view.tag(name, attributes)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,148 @@
1
+ module MetaTags
2
+ # Module contains helpers that normalize text meta tag values.
3
+ module TextNormalizer
4
+ # Normalize title value.
5
+ #
6
+ # @param [String] site_title site title.
7
+ # @param [String, Array<String>] title title string.
8
+ # @param [String] separator a string to join title parts with.
9
+ # @param [true,false] reverse whether title should be reversed.
10
+ # @return [Array<String>] array of title parts with tags removed.
11
+ #
12
+ def self.normalize_title(site_title, title, separator, reverse = false)
13
+ title = Array(title).flatten.map(&method(:strip_tags))
14
+ title.reject!(&:blank?)
15
+ site_title = strip_tags(site_title)
16
+ separator = strip_tags(separator)
17
+
18
+ if MetaTags.config.title_limit
19
+ limit = MetaTags.config.title_limit - separator.length
20
+ if limit > site_title.length
21
+ title = truncate_array(title, limit - site_title.length, separator)
22
+ else
23
+ site_title = truncate(site_title, limit)
24
+ # Site title is too long, we have to skip page title
25
+ title = []
26
+ end
27
+ end
28
+
29
+ title.unshift(site_title) if site_title.present?
30
+ title.reverse! if reverse
31
+ safe_join(title, separator)
32
+ end
33
+
34
+ # Normalize description value.
35
+ #
36
+ # @param [String] description description string.
37
+ # @return [String] text with tags removed, squashed spaces, truncated
38
+ # to 200 characters.
39
+ #
40
+ def self.normalize_description(description)
41
+ return '' if description.blank?
42
+ description = cleanup_string(description)
43
+ truncate(description, MetaTags.config.description_limit)
44
+ end
45
+
46
+ # Normalize keywords value.
47
+ #
48
+ # @param [String, Array<String>] keywords list of keywords as a string or Array.
49
+ # @return [String] list of keywords joined with comma, with tags removed.
50
+ #
51
+ def self.normalize_keywords(keywords)
52
+ return '' if keywords.blank?
53
+ keywords = cleanup_strings(keywords).each(&:downcase!)
54
+ separator = strip_tags MetaTags.config.keywords_separator
55
+
56
+ keywords = truncate_array(keywords, MetaTags.config.keywords_limit, separator)
57
+ safe_join(keywords, separator)
58
+ end
59
+
60
+ # Easy way to get access to Rails helpers.
61
+ #
62
+ # @return [ActionView::Base] proxy object to access Rails helpers.
63
+ #
64
+ def self.helpers
65
+ ActionController::Base.helpers
66
+ end
67
+
68
+ # Strips all HTML tags from the +html+, including comments.
69
+ #
70
+ # @param [String] string HTML string.
71
+ # @return [String] string with no HTML tags.
72
+ #
73
+ def self.strip_tags(string)
74
+ ERB::Util.html_escape helpers.strip_tags(string)
75
+ end
76
+
77
+ # This method returns a html safe string similar to what <tt>Array#join</tt>
78
+ # would return. All items in the array, including the supplied separator, are
79
+ # html escaped unless they are html safe, and the returned string is marked
80
+ # as html safe.
81
+ #
82
+ # @param [Array<String>] array list of strings to join.
83
+ # @param [String] sep separator to join strings with.
84
+ # @return [String] input strings joined together using a given separator.
85
+ #
86
+ def self.safe_join(array, sep = $,)
87
+ helpers.safe_join(array, sep)
88
+ end
89
+
90
+ # Removes HTML tags and squashes down all the spaces.
91
+ #
92
+ # @param [String] string input string.
93
+ # @return [String] input string with no HTML tags and consequent white
94
+ # space characters squashed into a single space.
95
+ #
96
+ def self.cleanup_string(string)
97
+ strip_tags(string).gsub(/\s+/, ' ').strip.html_safe
98
+ end
99
+
100
+ # Cleans multiple strings up.
101
+ #
102
+ # @param [Array<String>] strings input strings.
103
+ # @return [Array<String>] clean strings.
104
+ # @see cleanup_string
105
+ #
106
+ def self.cleanup_strings(strings)
107
+ Array(strings).flatten.map(&method(:cleanup_string))
108
+ end
109
+
110
+ # Truncates a string to a specific limit.
111
+ #
112
+ # @param [String] string input strings.
113
+ # @param [Integer,nil] limit characters number to truncate to.
114
+ # @param [String] natural_separator natural separator to truncate at.
115
+ # @return [String] truncated string.
116
+ #
117
+ def self.truncate(string, limit = nil, natural_separator = ' ')
118
+ string = helpers.truncate(string, length: limit, separator: natural_separator, omission: '') if limit
119
+ string
120
+ end
121
+
122
+ # Truncates a string to a specific limit.
123
+ #
124
+ # @param [Array<String>] string_array input strings.
125
+ # @param [Integer,nil] limit characters number to truncate to.
126
+ # @param [String] separator separator that will be used to join array later.
127
+ # @param [String] natural_separator natural separator to truncate at.
128
+ # @return [String] truncated string.
129
+ #
130
+ def self.truncate_array(string_array, limit = nil, separator = '', natural_separator = ' ')
131
+ return string_array if limit.nil? || limit == 0
132
+ length = 0
133
+ result = []
134
+ string_array.each do |string|
135
+ limit_left = limit - length - (result.any? ? separator.length : 0)
136
+ if string.length > limit_left
137
+ result << truncate(string, limit_left, natural_separator)
138
+ break
139
+ end
140
+ length += (result.any? ? separator.length : 0) + string.length
141
+ result << string
142
+ # No more strings will fit
143
+ break if length + separator.length >= limit
144
+ end
145
+ result
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,4 @@
1
+ module MetaTags
2
+ # Gem version.
3
+ VERSION = '1.0.0'
4
+ end
@@ -0,0 +1,205 @@
1
+ module MetaTags
2
+ # Contains methods to use in views and helpers.
3
+ #
4
+ module ViewHelper
5
+ # Get meta tags for the page.
6
+ def meta_tags
7
+ @meta_tags ||= MetaTagsCollection.new
8
+ end
9
+
10
+ # Set meta tags for the page.
11
+ #
12
+ # Method could be used several times, and all options passed will
13
+ # be merged. If you will set the same property several times, last one
14
+ # will take precedence.
15
+ #
16
+ # Usually you will not call this method directly. Use {#title}, {#keywords},
17
+ # {#description} for your daily tasks.
18
+ #
19
+ # @param [Hash] meta_tags list of meta tags. See {#display_meta_tags}
20
+ # for allowed options.
21
+ #
22
+ # @example
23
+ # set_meta_tags title: 'Login Page', description: 'Here you can login'
24
+ # set_meta_tags keywords: 'authorization, login'
25
+ #
26
+ # @see #display_meta_tags
27
+ #
28
+ def set_meta_tags(meta_tags = {})
29
+ self.meta_tags.update(meta_tags)
30
+ end
31
+
32
+ # Set the page title and return it back.
33
+ #
34
+ # This method is best suited for use in helpers. It sets the page title
35
+ # and returns it (or +headline+ if specified).
36
+ #
37
+ # @param [nil, String, Array] title page title. When passed as an
38
+ # +Array+, parts will be joined using configured separator value
39
+ # (see {#display_meta_tags}). When nil, current title will be returned.
40
+ # @param [String] headline the value to return from method. Useful
41
+ # for using this method in views to set both page title
42
+ # and the content of heading tag.
43
+ # @return [String] returns +title+ value or +headline+ if passed.
44
+ #
45
+ # @example Set HTML title to "Please login", return "Please login"
46
+ # title 'Login Page'
47
+ # @example Set HTML title to "Login Page", return "Please login"
48
+ # title 'Login Page', 'Please login'
49
+ # @example Set title as array of strings
50
+ # title title: ['part1', 'part2'] # => "part1 | part2"
51
+ # @example Get current title
52
+ # title
53
+ #
54
+ # @see #display_meta_tags
55
+ #
56
+ def title(title = nil, headline = '')
57
+ set_meta_tags(title: title) unless title.nil?
58
+ headline.blank? ? meta_tags[:title] : headline
59
+ end
60
+
61
+ # Set the page keywords.
62
+ #
63
+ # @param [String, Array] keywords meta keywords to render in HEAD
64
+ # section of the HTML document.
65
+ # @return [String, Array] passed value.
66
+ #
67
+ # @example
68
+ # keywords 'keyword1, keyword2'
69
+ # keywords %w(keyword1 keyword2)
70
+ #
71
+ # @see #display_meta_tags
72
+ #
73
+ def keywords(keywords)
74
+ set_meta_tags(keywords: keywords)
75
+ keywords
76
+ end
77
+
78
+ # Set the page description.
79
+ #
80
+ # @param [String] description page description to be set in HEAD section of
81
+ # the HTML document. Please note, any HTML tags will be stripped
82
+ # from output string, and string will be truncated to 200
83
+ # characters.
84
+ # @return [String] passed value.
85
+ #
86
+ # @example
87
+ # description 'This is login page'
88
+ #
89
+ # @see #display_meta_tags
90
+ #
91
+ def description(description)
92
+ set_meta_tags(description: description)
93
+ description
94
+ end
95
+
96
+ # Set the noindex meta tag
97
+ #
98
+ # @param [Boolean, String] noindex a noindex value.
99
+ # @return [Boolean, String] passed value.
100
+ #
101
+ # @example
102
+ # noindex true
103
+ # noindex 'googlebot'
104
+ #
105
+ # @see #display_meta_tags
106
+ #
107
+ def noindex(noindex = true)
108
+ set_meta_tags(noindex: noindex)
109
+ noindex
110
+ end
111
+
112
+ # Set the nofollow meta tag
113
+ #
114
+ # @param [Boolean, String] nofollow a nofollow value.
115
+ # @return [Boolean, String] passed value.
116
+ #
117
+ # @example
118
+ # nofollow true
119
+ # nofollow 'googlebot'
120
+ #
121
+ # @see #display_meta_tags
122
+ #
123
+ def nofollow(nofollow = true)
124
+ set_meta_tags(nofollow: nofollow)
125
+ nofollow
126
+ end
127
+
128
+ # Set the refresh meta tag
129
+ #
130
+ # @param [Integer, String] refresh a refresh value.
131
+ # @return [Integer, String] passed value.
132
+ #
133
+ # @example
134
+ # refresh 5
135
+ # refresh "5;url=http://www.example.com/"
136
+ #
137
+ # @see #display_meta_tags
138
+ #
139
+ def refresh(refresh)
140
+ set_meta_tags(refresh: refresh)
141
+ refresh
142
+ end
143
+
144
+ # Set default meta tag values and display meta tags. This method
145
+ # should be used in layout file.
146
+ #
147
+ # @param [Hash] defaults default meta tag values.
148
+ # @option default [String] :site (nil) site title;
149
+ # @option default [String] :title ("") page title;
150
+ # @option default [String] :description (nil) page description;
151
+ # @option default [String] :keywords (nil) page keywords;
152
+ # @option default [String, Boolean] :prefix (" ") text between site name and separator; when +false+, no prefix will be rendered;
153
+ # @option default [String] :separator ("|") text used to separate website name from page title;
154
+ # @option default [String, Boolean] :suffix (" ") text between separator and page title; when +false+, no suffix will be rendered;
155
+ # @option default [Boolean] :lowercase (false) when true, the page title will be lowercase;
156
+ # @option default [Boolean] :reverse (false) when true, the page and site names will be reversed;
157
+ # @option default [Boolean, String] :noindex (false) add noindex meta tag; when true, 'robots' will be used, otherwise the string will be used;
158
+ # @option default [Boolean, String] :nofollow (false) add nofollow meta tag; when true, 'robots' will be used, otherwise the string will be used;
159
+ # @option default [String] :canonical (nil) add canonical link tag.
160
+ # @option default [Hash] :alternate ({}) add alternate link tag.
161
+ # @option default [String] :prev (nil) add prev link tag;
162
+ # @option default [String] :next (nil) add next link tag.
163
+ # @option default [String] :author (nil) add author link tag;
164
+ # @option default [String] :publisher (nil) add publisher link tag.
165
+ # @option default [String, Integer] :refresh (nil) meta refresh tag;
166
+ # @option default [Hash] :open_graph ({}) add Open Graph meta tags.
167
+ # @option default [Hash] :open_search ({}) add Open Search link tag.
168
+ # @return [String] HTML meta tags to render in HEAD section of the
169
+ # HTML document.
170
+ #
171
+ # @example
172
+ # <head>
173
+ # <%= display_meta_tags site: 'My website' %>
174
+ # </head>
175
+ #
176
+ def display_meta_tags(defaults = {})
177
+ self.meta_tags.with_defaults(defaults) { Renderer.new(meta_tags).render(self) }
178
+ end
179
+
180
+ # Returns full page title as a string without surrounding <title> tag.
181
+ #
182
+ # The only case when you may need this helper is when you use pjax. This means
183
+ # that your layout file (with display_meta_tags helper) will not be rendered,
184
+ # so you have to pass default arguments like site title in here. You probably
185
+ # want to define helper with default options to minimize code duplication.
186
+ #
187
+ # @param [Hash] defaults list of meta tags.
188
+ # @option default [String] :site (nil) site title;
189
+ # @option default [String] :title ("") page title;
190
+ # @option default [String, Boolean] :prefix (" ") text between site name and separator; when +false+, no prefix will be rendered;
191
+ # @option default [String] :separator ("|") text used to separate website name from page title;
192
+ # @option default [String, Boolean] :suffix (" ") text between separator and page title; when +false+, no suffix will be rendered;
193
+ # @option default [Boolean] :lowercase (false) when true, the page name will be lowercase;
194
+ # @option default [Boolean] :reverse (false) when true, the page and site names will be reversed;
195
+ #
196
+ # @example
197
+ # <div data-page-container="true" title="<%= display_title title: 'My Page', site: 'PJAX Site' %>">
198
+ #
199
+ def display_title(defaults = {})
200
+ @meta_tags.full_title(defaults)
201
+ end
202
+
203
+ # safe_helper :display_meta_tags if defined?(:safe_helper)
204
+ end
205
+ end
data/meta-tags.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'meta_tags-rails/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'meta_tags-rails'
7
+ s.version = MetaTags::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.license = "MIT"
10
+ s.authors = ['Dmytro Shteflyuk', 'Yves Siegrist']
11
+ s.email = ['Elektron1c97@gmail.com']
12
+ s.homepage = 'http://github.com/Elektron1c97/meta_tags-rails'
13
+ s.summary = %q{Collection of SEO helpers for Ruby on Rails.}
14
+ s.description = %q{Search Engine Optimization (SEO) plugin for Ruby on Rails applications.}
15
+
16
+ s.add_dependency 'actionpack', '>= 3.0.0'
17
+
18
+ s.add_development_dependency 'rake'
19
+ s.add_development_dependency 'rspec', '~> 3.3.0'
20
+ s.add_development_dependency 'rspec-html-matchers'
21
+ s.add_development_dependency 'yard'
22
+ s.add_development_dependency 'bluecloth'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.extra_rdoc_files = ['README.md', 'CHANGELOG.md']
28
+ s.rdoc_options = ['--charset=UTF-8']
29
+ s.require_paths = ['lib']
30
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe MetaTags::Configuration do
4
+ it 'should be returned by MetaTags.config' do
5
+ expect(MetaTags.config).to be_instance_of(MetaTags::Configuration)
6
+ end
7
+
8
+ it 'should be yielded by MetaTags.configure' do
9
+ MetaTags.configure do |c|
10
+ expect(c).to be_instance_of(MetaTags::Configuration)
11
+ expect(c).to be(MetaTags.config)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ class MetaTagsController < ActionController::Base
4
+ attr_reader :rendered
5
+
6
+ def render_without_meta_tags
7
+ @rendered = true
8
+ end
9
+
10
+ def index
11
+ @page_title = 'title'
12
+ @page_keywords = 'key1, key2, key3'
13
+ @page_description = 'description'
14
+ render
15
+ end
16
+
17
+ public :set_meta_tags, :meta_tags
18
+ end
19
+
20
+ describe MetaTags::ControllerHelper do
21
+ subject { MetaTagsController.new }
22
+
23
+ context 'module' do
24
+ it 'should be mixed into ActionController::Base' do
25
+ expect(ActionController::Base.included_modules).to include(MetaTags::ControllerHelper)
26
+ end
27
+
28
+ it 'should respond to "set_meta_tags" helper' do
29
+ expect(subject).to respond_to(:set_meta_tags)
30
+ end
31
+ end
32
+
33
+ describe '.render' do
34
+ it 'should set meta tags from instance variables' do
35
+ subject.index
36
+ expect(subject.rendered).to be_truthy
37
+ expect(subject.meta_tags.meta_tags).to eq('title' => 'title', 'keywords' => 'key1, key2, key3', 'description' => 'description')
38
+ end
39
+ end
40
+
41
+ it_behaves_like '.set_meta_tags'
42
+ end