meta-tags 2.0.0 → 2.1.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.
- checksums.yaml +5 -13
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -1
- data/.travis.yml +7 -13
- data/CHANGELOG.md +18 -0
- data/Gemfile +5 -0
- data/README.md +163 -71
- data/Rakefile +2 -2
- data/lib/meta_tags.rb +18 -0
- data/lib/meta_tags/configuration.rb +21 -0
- data/lib/meta_tags/meta_tags_collection.rb +5 -10
- data/lib/meta_tags/renderer.rb +114 -37
- data/lib/meta_tags/text_normalizer.rb +71 -6
- data/lib/meta_tags/version.rb +1 -1
- data/lib/meta_tags/view_helper.rb +12 -11
- data/meta-tags.gemspec +2 -1
- data/spec/configuration_spec.rb +14 -0
- data/spec/controller_helper_spec.rb +4 -4
- data/spec/spec_helper.rb +69 -13
- data/spec/text_normalizer/normalize_title_spec.rb +43 -0
- data/spec/text_normalizer/truncate_array_spec.rb +60 -0
- data/spec/view_helper/charset_spec.rb +16 -0
- data/spec/view_helper/custom_spec.rb +67 -0
- data/spec/view_helper/description_spec.rb +61 -0
- data/spec/view_helper/icon_spec.rb +42 -0
- data/spec/view_helper/keywords_spec.rb +58 -0
- data/spec/view_helper/links_spec.rb +125 -0
- data/spec/view_helper/module_spec.rb +41 -0
- data/spec/view_helper/noindex_spec.rb +107 -0
- data/spec/view_helper/open_graph_spec.rb +86 -0
- data/spec/view_helper/open_search_spec.rb +33 -0
- data/spec/view_helper/refresh_spec.rb +32 -0
- data/spec/view_helper/title_spec.rb +155 -0
- data/spec/view_helper/twitter_spec.rb +31 -0
- data/spec/view_helper_spec.rb +57 -0
- metadata +53 -21
- data/spec/meta_tags_spec.rb +0 -570
data/Rakefile
CHANGED
data/lib/meta_tags.rb
CHANGED
@@ -3,10 +3,28 @@ require 'action_view'
|
|
3
3
|
|
4
4
|
# MetaTags gem namespace.
|
5
5
|
module MetaTags
|
6
|
+
# Returns MetaTags gem configuration.
|
7
|
+
#
|
8
|
+
def self.config
|
9
|
+
@@config ||= Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Configures MetaTags gem.
|
13
|
+
#
|
14
|
+
# @yield [Configuration] configuration object.
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# MetaTags.configure do |config|
|
18
|
+
# # config.title_limit = 100
|
19
|
+
# end
|
20
|
+
def self.configure
|
21
|
+
yield config
|
22
|
+
end
|
6
23
|
end
|
7
24
|
|
8
25
|
require 'meta_tags/version'
|
9
26
|
|
27
|
+
require 'meta_tags/configuration'
|
10
28
|
require 'meta_tags/controller_helper'
|
11
29
|
require 'meta_tags/meta_tags_collection'
|
12
30
|
require 'meta_tags/renderer'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MetaTags
|
2
|
+
# MetaTags configuration.
|
3
|
+
class Configuration
|
4
|
+
# How many characters to truncate title to.
|
5
|
+
attr_accessor :title_limit
|
6
|
+
# How many characters to truncate description to.
|
7
|
+
attr_accessor :description_limit
|
8
|
+
# How many characters to truncate keywords to.
|
9
|
+
attr_accessor :keywords_limit
|
10
|
+
# Keywords separator - a string to join keywords with.
|
11
|
+
attr_accessor :keywords_separator
|
12
|
+
|
13
|
+
# Initializes a new instance of Configuration class.
|
14
|
+
def initialize
|
15
|
+
@title_limit = 70
|
16
|
+
@description_limit = 160
|
17
|
+
@keywords_limit = 255
|
18
|
+
@keywords_separator = ', '
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -82,17 +82,12 @@ module MetaTags
|
|
82
82
|
# @return [String] page title.
|
83
83
|
#
|
84
84
|
def extract_full_title
|
85
|
-
|
86
|
-
|
85
|
+
site_title = extract(:site) || ''
|
86
|
+
title = extract_title || []
|
87
|
+
separator = extract_separator
|
88
|
+
reverse = extract(:reverse) === true
|
87
89
|
|
88
|
-
site_title
|
89
|
-
title.unshift(site_title) if site_title
|
90
|
-
title = TextNormalizer.normalize_title(title)
|
91
|
-
|
92
|
-
title.reverse! if extract(:reverse) === true
|
93
|
-
title = TextNormalizer.safe_join(title, separator)
|
94
|
-
|
95
|
-
title
|
90
|
+
TextNormalizer.normalize_title(site_title, title, separator, reverse)
|
96
91
|
end
|
97
92
|
|
98
93
|
# Extracts page title as an array of segments without site title and separators.
|
data/lib/meta_tags/renderer.rb
CHANGED
@@ -18,15 +18,18 @@ module MetaTags
|
|
18
18
|
def render(view)
|
19
19
|
tags = []
|
20
20
|
|
21
|
+
render_charset(tags)
|
21
22
|
render_title(tags)
|
22
|
-
|
23
|
-
|
23
|
+
render_icon(tags)
|
24
|
+
render_with_normalization(tags, :description)
|
25
|
+
render_with_normalization(tags, :keywords)
|
24
26
|
render_refresh(tags)
|
25
27
|
render_noindex(tags)
|
26
28
|
render_alternate(tags)
|
29
|
+
render_open_search(tags)
|
27
30
|
render_links(tags)
|
28
31
|
|
29
|
-
render_hash(tags, :
|
32
|
+
render_hash(tags, :og, name_key: :property)
|
30
33
|
render_hashes(tags)
|
31
34
|
render_custom(tags)
|
32
35
|
|
@@ -35,6 +38,17 @@ module MetaTags
|
|
35
38
|
|
36
39
|
protected
|
37
40
|
|
41
|
+
|
42
|
+
# Renders charset tag.
|
43
|
+
#
|
44
|
+
# @param [Array<Tag>] tags a buffer object to store tag in.
|
45
|
+
#
|
46
|
+
def render_charset(tags)
|
47
|
+
if charset = meta_tags.extract(:charset)
|
48
|
+
tags << Tag.new(:meta, charset: charset) if charset.present?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
38
52
|
# Renders title tag.
|
39
53
|
#
|
40
54
|
# @param [Array<Tag>] tags a buffer object to store tag in.
|
@@ -42,27 +56,37 @@ module MetaTags
|
|
42
56
|
def render_title(tags)
|
43
57
|
title = meta_tags.extract_full_title
|
44
58
|
normalized_meta_tags[:title] = title
|
45
|
-
tags << ContentTag.new(:title, :
|
59
|
+
tags << ContentTag.new(:title, content: title) if title.present?
|
46
60
|
end
|
47
61
|
|
48
|
-
# Renders
|
62
|
+
# Renders icon(s) tag.
|
49
63
|
#
|
50
64
|
# @param [Array<Tag>] tags a buffer object to store tag in.
|
51
65
|
#
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
def render_icon(tags)
|
67
|
+
if icon = meta_tags.extract(:icon)
|
68
|
+
if String === icon
|
69
|
+
tags << Tag.new(:link, rel: 'icon', href: icon, type: 'image/x-icon')
|
70
|
+
else
|
71
|
+
icon = [icon] if Hash === icon
|
72
|
+
icon.each do |icon_params|
|
73
|
+
icon_params = { rel: 'icon', type: 'image/x-icon' }.with_indifferent_access.merge(icon_params)
|
74
|
+
tags << Tag.new(:link, icon_params)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
56
78
|
end
|
57
79
|
|
58
|
-
# Renders
|
80
|
+
# Renders meta tag with normalization (should have a corresponding normalize_
|
81
|
+
# method in TextNormalizer).
|
59
82
|
#
|
60
83
|
# @param [Array<Tag>] tags a buffer object to store tag in.
|
84
|
+
# @see TextNormalizer
|
61
85
|
#
|
62
|
-
def
|
63
|
-
|
64
|
-
normalized_meta_tags[
|
65
|
-
tags << Tag.new(:meta, :name
|
86
|
+
def render_with_normalization(tags, name)
|
87
|
+
value = TextNormalizer.public_send("normalize_#{name}", meta_tags.extract(name))
|
88
|
+
normalized_meta_tags[name] = value
|
89
|
+
tags << Tag.new(:meta, name: name, content: value) if value.present?
|
66
90
|
end
|
67
91
|
|
68
92
|
# Renders noindex and nofollow meta tags.
|
@@ -71,7 +95,7 @@ module MetaTags
|
|
71
95
|
#
|
72
96
|
def render_noindex(tags)
|
73
97
|
meta_tags.extract_noindex.each do |name, content|
|
74
|
-
tags << Tag.new(:meta, :
|
98
|
+
tags << Tag.new(:meta, name: name, content: content) if content.present?
|
75
99
|
end
|
76
100
|
end
|
77
101
|
|
@@ -81,7 +105,7 @@ module MetaTags
|
|
81
105
|
#
|
82
106
|
def render_refresh(tags)
|
83
107
|
if refresh = meta_tags.extract(:refresh)
|
84
|
-
tags << Tag.new(:meta, 'http-equiv' => 'refresh', :
|
108
|
+
tags << Tag.new(:meta, 'http-equiv' => 'refresh', content: refresh.to_s) if refresh.present?
|
85
109
|
end
|
86
110
|
end
|
87
111
|
|
@@ -91,8 +115,29 @@ module MetaTags
|
|
91
115
|
#
|
92
116
|
def render_alternate(tags)
|
93
117
|
if alternate = meta_tags.extract(:alternate)
|
94
|
-
|
95
|
-
|
118
|
+
if Hash === alternate
|
119
|
+
alternate.each do |hreflang, href|
|
120
|
+
tags << Tag.new(:link, rel: 'alternate', href: href, hreflang: hreflang) if href.present?
|
121
|
+
end
|
122
|
+
elsif Array === alternate
|
123
|
+
alternate.each do |link_params|
|
124
|
+
tags << Tag.new(:link, { rel: 'alternate' }.with_indifferent_access.merge(link_params))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Renders open_search link tag.
|
131
|
+
#
|
132
|
+
# @param [Array<Tag>] tags a buffer object to store tag in.
|
133
|
+
#
|
134
|
+
def render_open_search(tags)
|
135
|
+
if open_search = meta_tags.extract(:open_search)
|
136
|
+
href = open_search[:href]
|
137
|
+
title = open_search[:title]
|
138
|
+
if href.present?
|
139
|
+
type = "application/opensearchdescription+xml"
|
140
|
+
tags << Tag.new(:link, rel: 'search', type: type, href: href, title: title)
|
96
141
|
end
|
97
142
|
end
|
98
143
|
end
|
@@ -106,7 +151,7 @@ module MetaTags
|
|
106
151
|
href = meta_tags.extract(tag_name)
|
107
152
|
if href.present?
|
108
153
|
@normalized_meta_tags[tag_name] = href
|
109
|
-
tags << Tag.new(:link, :
|
154
|
+
tags << Tag.new(:link, rel: tag_name, href: href)
|
110
155
|
end
|
111
156
|
end
|
112
157
|
end
|
@@ -118,7 +163,7 @@ module MetaTags
|
|
118
163
|
def render_hashes(tags, options = {})
|
119
164
|
meta_tags.meta_tags.each do |property, data|
|
120
165
|
if data.is_a?(Hash)
|
121
|
-
|
166
|
+
process_hash(tags, property, data, options)
|
122
167
|
meta_tags.extract(property)
|
123
168
|
end
|
124
169
|
end
|
@@ -131,7 +176,7 @@ module MetaTags
|
|
131
176
|
def render_hash(tags, key, options = {})
|
132
177
|
data = meta_tags.meta_tags[key]
|
133
178
|
if data.is_a?(Hash)
|
134
|
-
|
179
|
+
process_hash(tags, key, data, options)
|
135
180
|
meta_tags.extract(key)
|
136
181
|
end
|
137
182
|
end
|
@@ -143,7 +188,7 @@ module MetaTags
|
|
143
188
|
def render_custom(tags)
|
144
189
|
meta_tags.meta_tags.each do |name, data|
|
145
190
|
Array(data).each do |val|
|
146
|
-
tags << Tag.new(:meta, :
|
191
|
+
tags << Tag.new(:meta, name: name, content: val)
|
147
192
|
end
|
148
193
|
meta_tags.extract(name)
|
149
194
|
end
|
@@ -152,25 +197,57 @@ module MetaTags
|
|
152
197
|
# Recursive method to process all the hashes and arrays on meta tags
|
153
198
|
#
|
154
199
|
# @param [Array<Tag>] tags a buffer object to store tag in.
|
155
|
-
# @param [
|
156
|
-
# @param [String, Symbol] content text content or a symbol reference to
|
200
|
+
# @param [String, Symbol] property a Hash or a String to render as meta tag.
|
201
|
+
# @param [Hash, Array, String, Symbol] content text content or a symbol reference to
|
157
202
|
# top-level meta tag.
|
158
203
|
#
|
159
204
|
def process_tree(tags, property, content, options = {})
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
205
|
+
method = case content
|
206
|
+
when Hash
|
207
|
+
:process_hash
|
208
|
+
when Array
|
209
|
+
:process_array
|
210
|
+
else
|
211
|
+
:render_tag
|
212
|
+
end
|
213
|
+
send(method, tags, property, content, options)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Recursive method to process a hash with meta tags
|
217
|
+
#
|
218
|
+
# @param [Array<Tag>] tags a buffer object to store tag in.
|
219
|
+
# @param [String, Symbol] property a Hash or a String to render as meta tag.
|
220
|
+
# @param [Hash] content nested meta tag attributes.
|
221
|
+
#
|
222
|
+
def process_hash(tags, property, content, options = {})
|
223
|
+
content.each do |key, value|
|
224
|
+
key = key.to_s == '_' ? property : "#{property}:#{key}"
|
225
|
+
value = normalized_meta_tags[value] if value.is_a?(Symbol)
|
226
|
+
process_tree(tags, key, value, options)
|
173
227
|
end
|
174
228
|
end
|
229
|
+
|
230
|
+
# Recursive method to process a hash with meta tags
|
231
|
+
#
|
232
|
+
# @param [Array<Tag>] tags a buffer object to store tag in.
|
233
|
+
# @param [String, Symbol] property a Hash or a String to render as meta tag.
|
234
|
+
# @param [Array] content array of nested meta tag attributes or values.
|
235
|
+
#
|
236
|
+
def process_array(tags, property, content, options = {})
|
237
|
+
content.each { |v| process_tree(tags, property, v, options) }
|
238
|
+
end
|
239
|
+
|
240
|
+
# Recursive method to process a hash with meta tags
|
241
|
+
#
|
242
|
+
# @param [Array<Tag>] tags a buffer object to store tag in.
|
243
|
+
# @param [String, Symbol] name a Hash or a String to render as meta tag.
|
244
|
+
# @param [String, Symbol] value text content or a symbol reference to
|
245
|
+
# top-level meta tag.
|
246
|
+
#
|
247
|
+
def render_tag(tags, name, value, options = {})
|
248
|
+
name_key = options.fetch(:name_key, :name)
|
249
|
+
value_key = options.fetch(:value_key, :content)
|
250
|
+
tags << Tag.new(:meta, name_key => name.to_s, value_key => value) unless value.blank?
|
251
|
+
end
|
175
252
|
end
|
176
253
|
end
|
@@ -3,11 +3,32 @@ module MetaTags
|
|
3
3
|
module TextNormalizer
|
4
4
|
# Normalize title value.
|
5
5
|
#
|
6
|
+
# @param [String] site_title site title.
|
6
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.
|
7
10
|
# @return [Array<String>] array of title parts with tags removed.
|
8
11
|
#
|
9
|
-
def self.normalize_title(title)
|
10
|
-
Array(title).flatten.map(&method(:strip_tags))
|
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)
|
11
32
|
end
|
12
33
|
|
13
34
|
# Normalize description value.
|
@@ -18,7 +39,8 @@ module MetaTags
|
|
18
39
|
#
|
19
40
|
def self.normalize_description(description)
|
20
41
|
return '' if description.blank?
|
21
|
-
|
42
|
+
description = cleanup_string(description)
|
43
|
+
truncate(description, MetaTags.config.description_limit)
|
22
44
|
end
|
23
45
|
|
24
46
|
# Normalize keywords value.
|
@@ -28,7 +50,11 @@ module MetaTags
|
|
28
50
|
#
|
29
51
|
def self.normalize_keywords(keywords)
|
30
52
|
return '' if keywords.blank?
|
31
|
-
cleanup_strings(keywords).
|
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)
|
32
58
|
end
|
33
59
|
|
34
60
|
# Easy way to get access to Rails helpers.
|
@@ -45,7 +71,7 @@ module MetaTags
|
|
45
71
|
# @return [String] string with no HTML tags.
|
46
72
|
#
|
47
73
|
def self.strip_tags(string)
|
48
|
-
helpers.strip_tags(string)
|
74
|
+
ERB::Util.html_escape helpers.strip_tags(string)
|
49
75
|
end
|
50
76
|
|
51
77
|
# This method returns a html safe string similar to what <tt>Array#join</tt>
|
@@ -68,7 +94,7 @@ module MetaTags
|
|
68
94
|
# space characters squashed into a single space.
|
69
95
|
#
|
70
96
|
def self.cleanup_string(string)
|
71
|
-
strip_tags(string).gsub(/\s+/, ' ').strip
|
97
|
+
strip_tags(string).gsub(/\s+/, ' ').strip.html_safe
|
72
98
|
end
|
73
99
|
|
74
100
|
# Cleans multiple strings up.
|
@@ -76,8 +102,47 @@ module MetaTags
|
|
76
102
|
# @param [Array<String>] strings input strings.
|
77
103
|
# @return [Array<String>] clean strings.
|
78
104
|
# @see cleanup_string
|
105
|
+
#
|
79
106
|
def self.cleanup_strings(strings)
|
80
107
|
Array(strings).flatten.map(&method(:cleanup_string))
|
81
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
|
82
147
|
end
|
83
148
|
end
|