meta-tags 2.23.0 → 2.24.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +69 -0
- data/README.md +133 -17
- data/SECURITY.md +40 -0
- data/lib/generators/meta_tags/templates/config/initializers/meta_tags.rb +10 -0
- data/lib/meta_tags/configuration.rb +10 -0
- data/lib/meta_tags/meta_tags_collection.rb +93 -21
- data/lib/meta_tags/railtie.rb +4 -0
- data/lib/meta_tags/renderer.rb +47 -9
- data/lib/meta_tags/text_normalizer.rb +2 -1
- data/lib/meta_tags/version.rb +1 -1
- data/lib/meta_tags/view_helper.rb +3 -0
- data/lib/meta_tags.rb +6 -0
- data/sig/_private/rails_support.rbs +5 -0
- data/sig/lib/meta_tags/configuration.rbs +5 -3
- data/sig/lib/meta_tags/meta_tags_collection.rbs +8 -2
- data/sig/lib/meta_tags/renderer.rbs +11 -7
- data/sig/lib/meta_tags/tag.rbs +1 -1
- data/sig/lib/meta_tags/view_helper.rbs +1 -0
- data/sig/lib/meta_tags.rbs +2 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 566365edffbf0315633065a07584898e708104ed5657ae1b673302dcd10ed3b0
|
|
4
|
+
data.tar.gz: ffde1c27ef6745ad5ce3635118a63e4af9b3845dc4ee6ef4eb1c2747bd93f7e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec303b92dcd6e228fb13d23ce7222f9086e53f715efdccc587c298ed1ad0de838917d29e818a2549f3c91600264b523075772272ce5aa0ea8dd8e1c1ebd6d3c9
|
|
7
|
+
data.tar.gz: 9caa336eb5b685e16fe8474ff3aa70e79ceb931c8bba1ddab58db5947a67a4a45d92ccb1ce1d2a6c34a033a54ff8a704ae76ecc386057607bb6f8fd5388bd6d6
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.24.0 (September 1, 2026) [☰](https://github.com/kpumuk/meta-tags/compare/v2.23.0...v2.24.0)
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
- Added the `truncate_array_items_at_boundaries` option for `title` and `keywords` arrays. When enabled, multi-item arrays stop before an overflowing item instead of cutting that item in the middle. The first or only item can still be truncated, so a non-empty value does not become empty ([354](https://github.com/kpumuk/meta-tags/pull/354)).
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
MetaTags.configure do |config|
|
|
13
|
+
config.title_limit = 12
|
|
14
|
+
config.truncate_array_items_at_boundaries = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
display_meta_tags(title: ["Shoes", "Running", "Men"])
|
|
18
|
+
# => <title>Shoes</title>
|
|
19
|
+
# With the option disabled: <title>Shoes | Runn</title>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Added hash-based custom directives for `robots`, `googlebot`, and `bingbot`. `nil` and `true` produce valueless directives, `false` omits a directive, and other values use `name:value`; existing robots helpers and legacy scalar and array values remain supported ([224](https://github.com/kpumuk/meta-tags/pull/224), rebased in [361](https://github.com/kpumuk/meta-tags/pull/361), corrected in [406](https://github.com/kpumuk/meta-tags/pull/406)).
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
display_meta_tags(
|
|
26
|
+
robots: {
|
|
27
|
+
"max-snippet" => -1,
|
|
28
|
+
"max-video-preview" => -1
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
# => <meta name="robots" content="max-snippet:-1, max-video-preview:-1">
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Bugfixes:
|
|
35
|
+
|
|
36
|
+
- Made `skip_canonical_links_on_noindex` follow the directives that are actually rendered. Empty crawler lists and blank crawler names no longer suppress canonical links, while effective `noindex` directives in custom `robots`, `googlebot`, and `bingbot` values do ([405](https://github.com/kpumuk/meta-tags/pull/405), [413](https://github.com/kpumuk/meta-tags/pull/413), [416](https://github.com/kpumuk/meta-tags/pull/416)).
|
|
37
|
+
- Preserved legacy scalar and array custom robots values instead of treating every value as a directive hash. This also fixes valueless directives being serialized with a trailing colon and disabled directives being rendered as `name:false` ([406](https://github.com/kpumuk/meta-tags/pull/406)).
|
|
38
|
+
- Preserved an inherited `itemprop` on anonymous values inside recursively structured metadata, including arrays of Open Graph images. Named subproperties still do not inherit the attribute, and an explicit nested value still wins ([410](https://github.com/kpumuk/meta-tags/pull/410)).
|
|
39
|
+
- Suppressed icon and array-form alternate links when their URL is `nil` or blank, instead of rendering unusable `href=""` elements ([411](https://github.com/kpumuk/meta-tags/pull/411)).
|
|
40
|
+
- Fixed `MetaTagsCollection#page_title` when `site` is supplied through method defaults. It now excludes the site while `full_title` continues to include it ([412](https://github.com/kpumuk/meta-tags/pull/412)).
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
collection.page_title(site: "Site", title: "Page") # => "Page"
|
|
44
|
+
collection.full_title(site: "Site", title: "Page") # => "Site | Page"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- Deep-merged the `og` and `open_graph` aliases when both appear in one input hash, instead of silently discarding the canonical `og` value. `og` wins conflicting leaves in the same input; separate updates retain their existing last-update precedence ([414](https://github.com/kpumuk/meta-tags/pull/414)).
|
|
48
|
+
- Aligned the packaged RBS contracts with existing runtime behavior: `description_limit` accepts `nil`, `title_tag_attributes` accepts Rails-style nested arrays and numeric or boolean values, and recursive metadata accepts finite `Float` values ([415](https://github.com/kpumuk/meta-tags/pull/415), [418](https://github.com/kpumuk/meta-tags/pull/418), [419](https://github.com/kpumuk/meta-tags/pull/419)).
|
|
49
|
+
- Fixed the `lowercase` title option so it is consumed even when the page title is absent, rather than leaking into `<meta name="lowercase">`. String-like title values implementing `#to_str` are now coerced before lowercasing instead of raising `NoMethodError` ([420](https://github.com/kpumuk/meta-tags/pull/420)).
|
|
50
|
+
|
|
51
|
+
Changes:
|
|
52
|
+
|
|
53
|
+
- **MetaTags 3.0 migration:** direct Symbols in nested custom-tag arrays now emit a deprecation warning. MetaTags 2.x still renders them literally by default; set `resolve_symbolic_references_in_arrays = true` to opt in to 3.0 reference semantics now, and use Strings for values that should remain literal. Direct hash references, explicit `{_: ...}` references, top-level arrays, and specialized arrays keep their existing behavior ([409](https://github.com/kpumuk/meta-tags/pull/409)).
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
MetaTags.configure do |config|
|
|
57
|
+
config.resolve_symbolic_references_in_arrays = true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
display_meta_tags(
|
|
61
|
+
image_src: "https://example.com/image.png",
|
|
62
|
+
og: {image: [:image_src]}
|
|
63
|
+
)
|
|
64
|
+
# => <link rel="image_src" href="https://example.com/image.png">
|
|
65
|
+
# <meta property="og:image" content="https://example.com/image.png">
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- Made README rendering examples executable regression tests and repaired confirmed dead reference links, reducing the chance that documented output drifts from the renderer ([404](https://github.com/kpumuk/meta-tags/pull/404), [417](https://github.com/kpumuk/meta-tags/pull/417)).
|
|
69
|
+
- Made the `steep` Rake task propagate type-checking failures and reconciled the RBS signatures with the current Ruby implementation, so CI can no longer report success after Steep errors ([407](https://github.com/kpumuk/meta-tags/pull/407)).
|
|
70
|
+
- Refreshed the supported development lockfiles, split Ruby 3.1 compatibility locks where patched dependencies require newer Ruby, and added an advisory audit covering every committed supported lockfile ([408](https://github.com/kpumuk/meta-tags/pull/408)).
|
|
71
|
+
- Hardened GitHub Actions by removing untrusted checkouts from the privileged Dependabot workflow, declaring token permissions, adding stable required checks and a security policy, and enforcing OpenSSF Scorecards and pedantic zizmor checks ([357](https://github.com/kpumuk/meta-tags/pull/357), [358](https://github.com/kpumuk/meta-tags/pull/358), [359](https://github.com/kpumuk/meta-tags/pull/359), [360](https://github.com/kpumuk/meta-tags/pull/360), [362](https://github.com/kpumuk/meta-tags/pull/362), [374](https://github.com/kpumuk/meta-tags/pull/374)).
|
|
72
|
+
- Updated the GitHub Actions toolchain throughout the release cycle, including `actions/checkout`, artifact upload, GitHub App authentication, `ruby/setup-ruby`, RubyGems release automation, Scorecards, and zizmor ([367](https://github.com/kpumuk/meta-tags/pull/367), [376](https://github.com/kpumuk/meta-tags/pull/376), [397](https://github.com/kpumuk/meta-tags/pull/397), [399](https://github.com/kpumuk/meta-tags/pull/399), [401](https://github.com/kpumuk/meta-tags/pull/401), [402](https://github.com/kpumuk/meta-tags/pull/402), [403](https://github.com/kpumuk/meta-tags/pull/403)).
|
|
73
|
+
|
|
5
74
|
## 2.23.0 (March 16, 2026) [☰](https://github.com/kpumuk/meta-tags/compare/v2.22.3...v2.23.0)
|
|
6
75
|
|
|
7
76
|
Bugfixes:
|
data/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/kpumuk/meta-tags/actions/workflows/tests.yml)
|
|
4
4
|
[](https://badge.fury.io/rb/meta-tags)
|
|
5
|
+
[](https://scorecard.dev/viewer/?uri=github.com/kpumuk/meta-tags)
|
|
5
6
|
[](https://github.com/testdouble/standard)
|
|
6
7
|
[](https://badge.fury.io/rb/meta-tags)
|
|
7
8
|
[](https://github.com/kpumuk/meta-tags/blob/main/CHANGELOG.md)
|
|
@@ -40,6 +41,25 @@ To override the defaults, create an initializer `config/initializers/meta_tags.r
|
|
|
40
41
|
rails generate meta_tags:install
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
When a truncation limit is reached, arrays passed to `title` or `keywords` can
|
|
45
|
+
either partially truncate the last item or stop at item boundaries. Set
|
|
46
|
+
`config.truncate_array_items_at_boundaries = true` to preserve whole items for
|
|
47
|
+
multi-item arrays. Single-item arrays are still truncated normally.
|
|
48
|
+
|
|
49
|
+
Symbols in nested custom tag arrays are literal values in MetaTags 2.x.
|
|
50
|
+
MetaTags 3.0 will use them to look up normalized top-level tags. You can turn
|
|
51
|
+
on this behavior now. Until then, each nested array that contains a direct
|
|
52
|
+
Symbol value emits a deprecation warning:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
MetaTags.configure do |config|
|
|
56
|
+
config.resolve_symbolic_references_in_arrays = true
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
When this option is on, use Strings for literal array values. MetaTags does not
|
|
61
|
+
render a tag when a Symbol has no match.
|
|
62
|
+
|
|
43
63
|
By default, meta tags are rendered with the key `name`. However, some meta tags are required to use `property` instead (like Facebook Open Graph object). The MetaTags gem allows you to configure which tags to render with the `property` attribute. The pre-configured list includes all possible Facebook Open Graph object types by default, but you can add your own in case you need it.
|
|
44
64
|
|
|
45
65
|
## MetaTags Usage
|
|
@@ -148,7 +168,7 @@ If you want to set the title and display another text, use this:
|
|
|
148
168
|
|
|
149
169
|
### Allowed options for `display_meta_tags` and `set_meta_tags` methods
|
|
150
170
|
|
|
151
|
-
Use these options to customize the
|
|
171
|
+
Use these options to customize the generated tags:
|
|
152
172
|
|
|
153
173
|
| Option | Description |
|
|
154
174
|
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -167,6 +187,9 @@ Use these options to customize the title format:
|
|
|
167
187
|
| `:nofollow` | Add nofollow meta tag; when true, "robots" will be used; accepts a string with a robot name or an array of strings |
|
|
168
188
|
| `:follow` | Add follow meta tag; when true, "robots" will be used; accepts a string with a robot name or an array of strings |
|
|
169
189
|
| `:noarchive` | Add noarchive meta tag; when true, "robots" will be used; accepts a string with a robot name or an array of strings |
|
|
190
|
+
| `:robots` | Add custom directives to the `robots` meta tag (Hash) |
|
|
191
|
+
| `:googlebot` | Add custom directives to the `googlebot` meta tag (Hash) |
|
|
192
|
+
| `:bingbot` | Add custom directives to the `bingbot` meta tag (Hash) |
|
|
170
193
|
| `:canonical` | Add canonical link tag |
|
|
171
194
|
| `:prev` | Add legacy prev pagination link tag |
|
|
172
195
|
| `:next` | Add legacy next pagination link tag |
|
|
@@ -297,6 +320,8 @@ These tags still matter for search snippets, canonicalization, robots directives
|
|
|
297
320
|
|
|
298
321
|
Page titles help browsers, social previews, and search engines understand the page. Use unique, descriptive titles that match the visible page content.
|
|
299
322
|
|
|
323
|
+
<!-- executable-example: titles -->
|
|
324
|
+
|
|
300
325
|
```ruby
|
|
301
326
|
set_meta_tags title: "Member Login"
|
|
302
327
|
# <title>Member Login</title>
|
|
@@ -319,6 +344,8 @@ Description meta tags help search engines generate snippets, but search engines
|
|
|
319
344
|
|
|
320
345
|
Below is an example of how to set a description tag using Ruby:
|
|
321
346
|
|
|
347
|
+
<!-- executable-example: description -->
|
|
348
|
+
|
|
322
349
|
```ruby
|
|
323
350
|
set_meta_tags description: "This is a sample description"
|
|
324
351
|
# <meta name="description" content="This is a sample description">
|
|
@@ -336,6 +363,8 @@ Further reading:
|
|
|
336
363
|
|
|
337
364
|
The `keywords` tag is a legacy feature. MetaTags still supports it for backwards compatibility and for systems that still read it, but it is not a modern web SEO signal.
|
|
338
365
|
|
|
366
|
+
<!-- executable-example: keywords -->
|
|
367
|
+
|
|
339
368
|
```ruby
|
|
340
369
|
set_meta_tags keywords: %w[keyword1 keyword2 keyword3]
|
|
341
370
|
# <meta name="keywords" content="keyword1, keyword2, keyword3">
|
|
@@ -352,6 +381,8 @@ Further reading:
|
|
|
352
381
|
|
|
353
382
|
By using the noindex meta tag, you can signal to search engines not to include specific pages in their indexes.
|
|
354
383
|
|
|
384
|
+
<!-- executable-example: noindex -->
|
|
385
|
+
|
|
355
386
|
```ruby
|
|
356
387
|
set_meta_tags noindex: true
|
|
357
388
|
# <meta name="robots" content="noindex">
|
|
@@ -371,6 +402,8 @@ Further reading:
|
|
|
371
402
|
|
|
372
403
|
In most cases, you do not need to emit `index` explicitly because it is already the default for crawlable pages.
|
|
373
404
|
|
|
405
|
+
<!-- executable-example: index -->
|
|
406
|
+
|
|
374
407
|
```ruby
|
|
375
408
|
set_meta_tags index: true
|
|
376
409
|
# <meta name="robots" content="index">
|
|
@@ -380,6 +413,8 @@ set_meta_tags index: true
|
|
|
380
413
|
|
|
381
414
|
Nofollow meta tags tell a search engine not to follow the links on a specific page. It is entirely possible that a robot might find the same links on another page without a nofollow attribute, perhaps on another site, and still arrive at your undesired page.
|
|
382
415
|
|
|
416
|
+
<!-- executable-example: nofollow -->
|
|
417
|
+
|
|
383
418
|
```ruby
|
|
384
419
|
set_meta_tags nofollow: true
|
|
385
420
|
# <meta name="robots" content="nofollow">
|
|
@@ -389,7 +424,7 @@ set_meta_tags nofollow: "googlebot"
|
|
|
389
424
|
|
|
390
425
|
Further reading:
|
|
391
426
|
|
|
392
|
-
- [About rel="nofollow"](
|
|
427
|
+
- [About rel="nofollow"](https://developers.google.com/search/docs/crawling-indexing/qualify-outbound-links)
|
|
393
428
|
- [Meta tags](http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=79812)
|
|
394
429
|
- [Google Search Central: robots meta tag and X-Robots-Tag](https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag)
|
|
395
430
|
|
|
@@ -397,6 +432,8 @@ Further reading:
|
|
|
397
432
|
|
|
398
433
|
You can use `follow` with `noindex` if you need that combination, but most pages do not need an explicit `follow` tag because it is also the default behavior.
|
|
399
434
|
|
|
435
|
+
<!-- executable-example: follow -->
|
|
436
|
+
|
|
400
437
|
```ruby
|
|
401
438
|
set_meta_tags noindex: true, follow: true
|
|
402
439
|
# <meta name="robots" content="noindex, follow">
|
|
@@ -404,6 +441,39 @@ set_meta_tags noindex: true, follow: true
|
|
|
404
441
|
|
|
405
442
|
This tag will prevent search engines from indexing this specific page, but it will still allow them to crawl and index the remaining pages on your website.
|
|
406
443
|
|
|
444
|
+
### Robots
|
|
445
|
+
|
|
446
|
+
Use the `robots`, `googlebot`, and `bingbot` hashes when you need directives beyond `index`, `noindex`, `follow`, `nofollow`, and `noarchive`.
|
|
447
|
+
|
|
448
|
+
This is useful for directives such as `max-snippet`, `max-video-preview`, and `unavailable_after`.
|
|
449
|
+
|
|
450
|
+
<!-- executable-example: robots -->
|
|
451
|
+
|
|
452
|
+
```ruby
|
|
453
|
+
set_meta_tags robots: {
|
|
454
|
+
"max-snippet" => -1,
|
|
455
|
+
"max-video-preview" => -1
|
|
456
|
+
}
|
|
457
|
+
# <meta name="robots" content="max-snippet:-1, max-video-preview:-1">
|
|
458
|
+
|
|
459
|
+
set_meta_tags googlebot: {
|
|
460
|
+
unavailable_after: "2026-12-31"
|
|
461
|
+
}
|
|
462
|
+
# <meta name="googlebot" content="unavailable_after:2026-12-31">
|
|
463
|
+
|
|
464
|
+
set_meta_tags bingbot: {
|
|
465
|
+
"max-image-preview" => "large"
|
|
466
|
+
}
|
|
467
|
+
# <meta name="bingbot" content="max-image-preview:large">
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
These hashes are merged with the existing robots helpers, so `noindex`, `nofollow`, and similar directives still render first for the same meta tag.
|
|
471
|
+
|
|
472
|
+
Further reading:
|
|
473
|
+
|
|
474
|
+
- [Google Search Central: robots meta tag and X-Robots-Tag](https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag)
|
|
475
|
+
- [Bing Webmaster Guidelines: robots meta tag support](https://www.bing.com/webmasters/help/which-robots-metatags-does-bing-support-5198d240)
|
|
476
|
+
|
|
407
477
|
### Canonical URL
|
|
408
478
|
|
|
409
479
|
Canonical link elements help search engines consolidate duplicate or near-duplicate URLs under one preferred URL. They are a signal, not a guarantee.
|
|
@@ -411,6 +481,8 @@ Canonical link elements help search engines consolidate duplicate or near-duplic
|
|
|
411
481
|
> [!NOTE]
|
|
412
482
|
> If your goal is duplicate consolidation, prefer a canonical URL over `noindex`. If you do not want to mix canonical with `noindex`, set `MetaTags.config.skip_canonical_links_on_noindex = true`.
|
|
413
483
|
|
|
484
|
+
<!-- executable-example: canonical -->
|
|
485
|
+
|
|
414
486
|
```ruby
|
|
415
487
|
set_meta_tags canonical: "http://yoursite.com/canonical/url"
|
|
416
488
|
# <link rel="canonical" href="http://yoursite.com/canonical/url">
|
|
@@ -426,17 +498,19 @@ Further reading:
|
|
|
426
498
|
|
|
427
499
|
A favicon (short for Favorite icon), also known as a shortcut icon, website icon, tab icon, or bookmark icon, is a file containing one or more small icons, most commonly 16x16 pixels, associated with a particular website or web page.
|
|
428
500
|
|
|
501
|
+
<!-- executable-example: icons -->
|
|
502
|
+
|
|
429
503
|
```ruby
|
|
430
504
|
set_meta_tags icon: "/favicon.ico"
|
|
431
|
-
# <link rel="icon"
|
|
432
|
-
set_meta_tags icon: "/favicon.png", type: "image/png"
|
|
433
|
-
# <link rel="icon"
|
|
505
|
+
# <link rel="icon" type="image/x-icon" href="/favicon.ico">
|
|
506
|
+
set_meta_tags icon: {href: "/favicon.png", type: "image/png"}
|
|
507
|
+
# <link rel="icon" type="image/png" href="/favicon.png">
|
|
434
508
|
set_meta_tags icon: [
|
|
435
509
|
{href: "/images/icons/icon_96.png", sizes: "32x32 96x96", type: "image/png"},
|
|
436
510
|
{href: "/images/icons/icon_itouch_precomp_32.png", rel: "apple-touch-icon-precomposed", sizes: "32x32", type: "image/png"}
|
|
437
511
|
]
|
|
438
|
-
# <link rel="icon" href="/images/icons/icon_96.png"
|
|
439
|
-
# <link rel="apple-touch-icon-precomposed" href="/images/icons/icon_itouch_precomp_32.png"
|
|
512
|
+
# <link rel="icon" type="image/png" href="/images/icons/icon_96.png" sizes="32x32 96x96">
|
|
513
|
+
# <link rel="apple-touch-icon-precomposed" type="image/png" href="/images/icons/icon_itouch_precomp_32.png" sizes="32x32">
|
|
440
514
|
```
|
|
441
515
|
|
|
442
516
|
Further reading:
|
|
@@ -448,6 +522,8 @@ Further reading:
|
|
|
448
522
|
|
|
449
523
|
Alternate link elements tell a search engine when there is content that's translated or targeted to users in a certain region.
|
|
450
524
|
|
|
525
|
+
<!-- executable-example: alternate -->
|
|
526
|
+
|
|
451
527
|
```ruby
|
|
452
528
|
set_meta_tags alternate: {"fr" => "http://yoursite.fr/alternate/url"}
|
|
453
529
|
# <link rel="alternate" href="http://yoursite.fr/alternate/url" hreflang="fr">
|
|
@@ -478,6 +554,8 @@ Further reading:
|
|
|
478
554
|
|
|
479
555
|
Previous and next links can describe a paginated sequence for browsers, feed readers, or custom consumers that still read them. Google no longer uses `rel="prev"` and `rel="next"` as an indexing signal, so treat them as optional interoperability metadata, not core SEO guidance.
|
|
480
556
|
|
|
557
|
+
<!-- executable-example: pagination -->
|
|
558
|
+
|
|
481
559
|
```ruby
|
|
482
560
|
set_meta_tags prev: "http://yoursite.com/url?page=1"
|
|
483
561
|
# <link rel="prev" href="http://yoursite.com/url?page=1">
|
|
@@ -487,14 +565,15 @@ set_meta_tags next: "http://yoursite.com/url?page=3"
|
|
|
487
565
|
|
|
488
566
|
Further reading:
|
|
489
567
|
|
|
490
|
-
- [Pagination](
|
|
491
|
-
- [Pagination with rel="next" and rel="prev"](http://googlewebmastercentral.blogspot.ca/2011/09/pagination-with-relnext-and-relprev.html)
|
|
568
|
+
- [Pagination with rel="next" and rel="prev"](https://developers.google.com/search/blog/2011/09/pagination-with-relnext-and-relprev)
|
|
492
569
|
- [Google Search Central: pagination, incremental page loading, and infinite scroll](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading)
|
|
493
570
|
|
|
494
571
|
### image_src links
|
|
495
572
|
|
|
496
573
|
`image_src` is a legacy share hint. Modern social sharing generally relies on Open Graph images and platform-specific card tags instead.
|
|
497
574
|
|
|
575
|
+
<!-- executable-example: image-src -->
|
|
576
|
+
|
|
498
577
|
```ruby
|
|
499
578
|
set_meta_tags image_src: "http://yoursite.com/icons/icon_32.png"
|
|
500
579
|
# <link rel="image_src" href="http://yoursite.com/icons/icon_32.png">
|
|
@@ -504,8 +583,13 @@ set_meta_tags image_src: "http://yoursite.com/icons/icon_32.png"
|
|
|
504
583
|
|
|
505
584
|
If your application still serves AMP pages, you can link the AMP version from the canonical page with `amphtml`. This is an optional legacy integration, not a general SEO requirement.
|
|
506
585
|
|
|
586
|
+
In a routed Rails view, you can generate the absolute URL with
|
|
587
|
+
`url_for(format: :amp, only_path: false)`.
|
|
588
|
+
|
|
589
|
+
<!-- executable-example: amphtml -->
|
|
590
|
+
|
|
507
591
|
```ruby
|
|
508
|
-
set_meta_tags amphtml:
|
|
592
|
+
set_meta_tags amphtml: "https://www.example.com/document.amp"
|
|
509
593
|
# <link rel="amphtml" href="https://www.example.com/document.amp">
|
|
510
594
|
```
|
|
511
595
|
|
|
@@ -517,6 +601,8 @@ To link back to the normal version, use the `canonical` tag.
|
|
|
517
601
|
|
|
518
602
|
By including the `rel="manifest"` attribute in the `<link>` element of an HTML page, you can specify the location of the manifest file that describes the web application. This allows the browser to understand that the web page is an application and to provide features like offline access and the ability to add the application to the home screen of a mobile device.
|
|
519
603
|
|
|
604
|
+
<!-- executable-example: manifest -->
|
|
605
|
+
|
|
520
606
|
```ruby
|
|
521
607
|
set_meta_tags manifest: "manifest.json"
|
|
522
608
|
# <link rel="manifest" href="manifest.json">
|
|
@@ -528,11 +614,13 @@ set_meta_tags manifest: "manifest.json"
|
|
|
528
614
|
|
|
529
615
|
Meta refresh is a method of instructing a web browser to automatically refresh the current web page or frame after a given time interval. It is also possible to instruct the browser to fetch a different URL when the page is refreshed, by including the alternative URL in the content parameter. By setting the refresh time interval to zero (or a very low value), this allows meta refresh to be used as a method of URL redirection.
|
|
530
616
|
|
|
617
|
+
<!-- executable-example: refresh -->
|
|
618
|
+
|
|
531
619
|
```ruby
|
|
532
620
|
set_meta_tags refresh: 5
|
|
533
|
-
# <meta
|
|
621
|
+
# <meta http-equiv="refresh" content="5">
|
|
534
622
|
set_meta_tags refresh: "5;url=http://example.com"
|
|
535
|
-
# <meta content="5;url=http://example.com"
|
|
623
|
+
# <meta http-equiv="refresh" content="5;url=http://example.com">
|
|
536
624
|
```
|
|
537
625
|
|
|
538
626
|
Further reading:
|
|
@@ -544,23 +632,27 @@ Further reading:
|
|
|
544
632
|
|
|
545
633
|
Open Search is a link element used to describe a search engine in a standard and accessible format.
|
|
546
634
|
|
|
635
|
+
<!-- executable-example: open-search -->
|
|
636
|
+
|
|
547
637
|
```ruby
|
|
548
638
|
set_meta_tags open_search: {
|
|
549
639
|
title: "Open Search",
|
|
550
640
|
href: "/opensearch.xml"
|
|
551
641
|
}
|
|
552
|
-
# <link
|
|
642
|
+
# <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Open Search">
|
|
553
643
|
```
|
|
554
644
|
|
|
555
645
|
Further reading:
|
|
556
646
|
|
|
557
|
-
- [OpenSearch specs](
|
|
647
|
+
- [OpenSearch specs](https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md)
|
|
558
648
|
- [OpenSearch wiki](http://en.wikipedia.org/wiki/OpenSearch)
|
|
559
649
|
|
|
560
650
|
### Hashes
|
|
561
651
|
|
|
562
652
|
Any namespace can be created by simply passing a symbol name and a Hash. For example:
|
|
563
653
|
|
|
654
|
+
<!-- executable-example: hashes -->
|
|
655
|
+
|
|
564
656
|
```ruby
|
|
565
657
|
set_meta_tags foo: {
|
|
566
658
|
bar: "lorem",
|
|
@@ -568,14 +660,20 @@ set_meta_tags foo: {
|
|
|
568
660
|
qux: "ipsum"
|
|
569
661
|
}
|
|
570
662
|
}
|
|
571
|
-
# <meta
|
|
572
|
-
# <meta
|
|
663
|
+
# <meta name="foo:bar" content="lorem">
|
|
664
|
+
# <meta name="foo:baz:qux" content="ipsum">
|
|
573
665
|
```
|
|
574
666
|
|
|
667
|
+
Arbitrary namespaces use the `name` attribute by default. To render `foo` and
|
|
668
|
+
its colon-delimited children with `property`, add `foo` to
|
|
669
|
+
`MetaTags.config.property_tags` in your initializer.
|
|
670
|
+
|
|
575
671
|
### Arrays
|
|
576
672
|
|
|
577
673
|
Repeated meta tags can be easily created by using an Array within a Hash. For example:
|
|
578
674
|
|
|
675
|
+
<!-- executable-example: arrays -->
|
|
676
|
+
|
|
579
677
|
```ruby
|
|
580
678
|
set_meta_tags og: {
|
|
581
679
|
image: ["http://example.com/rock.jpg", "http://example.com/rock2.jpg"]
|
|
@@ -588,6 +686,8 @@ set_meta_tags og: {
|
|
|
588
686
|
|
|
589
687
|
To turn your web pages into graph objects, you'll need to add Open Graph protocol `<meta>` tags to your webpages. The tags allow you to specify structured information about your web pages. The more information you provide, the more opportunities your web pages can be surfaced within Facebook today and in the future. Here's an example for a movie page:
|
|
590
688
|
|
|
689
|
+
<!-- executable-example: open-graph -->
|
|
690
|
+
|
|
591
691
|
```ruby
|
|
592
692
|
set_meta_tags og: {
|
|
593
693
|
title: "The Rock",
|
|
@@ -610,11 +710,13 @@ set_meta_tags og: {
|
|
|
610
710
|
|
|
611
711
|
Multiple images declared as an **array** (look at the `_` character):
|
|
612
712
|
|
|
713
|
+
<!-- executable-example: open-graph-images -->
|
|
714
|
+
|
|
613
715
|
```ruby
|
|
614
716
|
set_meta_tags og: {
|
|
615
717
|
title: "Two structured image properties",
|
|
616
718
|
type: "website",
|
|
617
|
-
url: "
|
|
719
|
+
url: "http://examples.opengraphprotocol.us/image-array.html",
|
|
618
720
|
image: [
|
|
619
721
|
{
|
|
620
722
|
_: "http://examples.opengraphprotocol.us/media/images/75.png",
|
|
@@ -641,6 +743,8 @@ set_meta_tags og: {
|
|
|
641
743
|
|
|
642
744
|
Article meta tags are supported too:
|
|
643
745
|
|
|
746
|
+
<!-- executable-example: article -->
|
|
747
|
+
|
|
644
748
|
```ruby
|
|
645
749
|
set_meta_tags article: {
|
|
646
750
|
published_time: "2013-09-17T05:59:00+01:00",
|
|
@@ -665,6 +769,8 @@ Further reading:
|
|
|
665
769
|
|
|
666
770
|
X cards let links shared on X show richer previews. The metadata namespace is still `twitter:*`. Here is a simple summary card example:
|
|
667
771
|
|
|
772
|
+
<!-- executable-example: twitter -->
|
|
773
|
+
|
|
668
774
|
```ruby
|
|
669
775
|
set_meta_tags twitter: {
|
|
670
776
|
card: "summary",
|
|
@@ -678,6 +784,8 @@ If you already publish Open Graph tags, you can often keep the X-specific tags m
|
|
|
678
784
|
|
|
679
785
|
When you need to generate nested `twitter:image:*` tags, the `twitter:image` property is a string while sub-properties can be expressed as a `Hash` in MetaTags:
|
|
680
786
|
|
|
787
|
+
<!-- executable-example: twitter-image -->
|
|
788
|
+
|
|
681
789
|
```ruby
|
|
682
790
|
set_meta_tags twitter: {
|
|
683
791
|
card: "summary_large_image",
|
|
@@ -697,6 +805,8 @@ set_meta_tags twitter: {
|
|
|
697
805
|
|
|
698
806
|
A special parameter `itemprop` can be used on an "anonymous" tag "\_" to generate the "itemprop" HTML attribute:
|
|
699
807
|
|
|
808
|
+
<!-- executable-example: twitter-itemprop -->
|
|
809
|
+
|
|
700
810
|
```ruby
|
|
701
811
|
set_meta_tags twitter: {
|
|
702
812
|
card: "summary_large_image",
|
|
@@ -723,6 +833,8 @@ Further reading:
|
|
|
723
833
|
|
|
724
834
|
App Links is an open cross-platform solution for deep linking to content in your mobile app. Here's an example of iOS app integration:
|
|
725
835
|
|
|
836
|
+
<!-- executable-example: app-links -->
|
|
837
|
+
|
|
726
838
|
```ruby
|
|
727
839
|
set_meta_tags al: {
|
|
728
840
|
ios: {
|
|
@@ -746,6 +858,8 @@ Starting from version 1.3.1, you can specify arbitrary meta tags, and they will
|
|
|
746
858
|
|
|
747
859
|
Example:
|
|
748
860
|
|
|
861
|
+
<!-- executable-example: custom-author -->
|
|
862
|
+
|
|
749
863
|
```ruby
|
|
750
864
|
set_meta_tags author: "Dmytro Shteflyuk"
|
|
751
865
|
# <meta name="author" content="Dmytro Shteflyuk">
|
|
@@ -753,6 +867,8 @@ set_meta_tags author: "Dmytro Shteflyuk"
|
|
|
753
867
|
|
|
754
868
|
You can also specify the value as an Array, and the values will be displayed as a list of `meta` tags:
|
|
755
869
|
|
|
870
|
+
<!-- executable-example: custom-author-array -->
|
|
871
|
+
|
|
756
872
|
```ruby
|
|
757
873
|
set_meta_tags author: ["Dmytro Shteflyuk", "John Doe"]
|
|
758
874
|
# <meta name="author" content="Dmytro Shteflyuk">
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Security fixes are provided for the latest released version of `meta-tags`.
|
|
6
|
+
|
|
7
|
+
Older releases may not receive security updates. If you are reporting a vulnerability, please confirm whether it affects the latest release.
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
Please do not report security issues through public GitHub issues, discussions, or pull requests.
|
|
12
|
+
|
|
13
|
+
Use GitHub's private vulnerability reporting feature in the repository's `Security` tab to report a vulnerability.
|
|
14
|
+
|
|
15
|
+
If private reporting through GitHub is unavailable for any reason, send a report by email to `kpumuk@kpumuk.info` with `SECURITY` in the subject line.
|
|
16
|
+
|
|
17
|
+
When possible, include:
|
|
18
|
+
|
|
19
|
+
- a short description of the issue
|
|
20
|
+
- the affected version, commit, or tag
|
|
21
|
+
- any required environment or configuration details
|
|
22
|
+
- step-by-step reproduction instructions
|
|
23
|
+
- proof-of-concept code, logs, or screenshots
|
|
24
|
+
- an explanation of the likely impact
|
|
25
|
+
|
|
26
|
+
## Disclosure Policy
|
|
27
|
+
|
|
28
|
+
Please allow a reasonable amount of time to investigate and prepare a fix before making the issue public.
|
|
29
|
+
|
|
30
|
+
If the report is confirmed, the fix will be released as soon as practical. Public disclosure will generally happen through a GitHub security advisory and/or release notes after a fix is available.
|
|
31
|
+
|
|
32
|
+
## Scope Notes
|
|
33
|
+
|
|
34
|
+
`meta-tags` is a Ruby gem for Rails applications that renders HTML metadata such as titles, descriptions, canonical links, robots directives, and social tags.
|
|
35
|
+
|
|
36
|
+
Some behavior may depend on the host Rails application, layouts, templates, framework version, gem version, or deployment/runtime configuration. If a report depends on a specific setup, include those details.
|
|
37
|
+
|
|
38
|
+
## Response Expectations
|
|
39
|
+
|
|
40
|
+
This is a single-maintainer project, so response times may vary. Good-faith reports are appreciated, and I will try to acknowledge valid reports as quickly as practical.
|
|
@@ -18,6 +18,11 @@ MetaTags.configure do |config|
|
|
|
18
18
|
# a Unicode space (/\p{Space}/).
|
|
19
19
|
# config.truncate_on_natural_separator = " "
|
|
20
20
|
|
|
21
|
+
# When true, arrays passed to `title` and `keywords` stop at item
|
|
22
|
+
# boundaries instead of partially truncating the overflowing item.
|
|
23
|
+
# Single-item arrays are still truncated normally. Default is false.
|
|
24
|
+
# config.truncate_array_items_at_boundaries = false
|
|
25
|
+
|
|
21
26
|
# Maximum length of the page description. Default is 300.
|
|
22
27
|
# Set to nil or 0 to remove limits.
|
|
23
28
|
# config.description_limit = 300
|
|
@@ -38,6 +43,11 @@ MetaTags.configure do |config|
|
|
|
38
43
|
# Default is false.
|
|
39
44
|
# config.minify_output = false
|
|
40
45
|
|
|
46
|
+
# When true, Symbols inside nested meta tag arrays resolve as references to
|
|
47
|
+
# normalized top-level tags. Default is false until MetaTags 3.0, when this
|
|
48
|
+
# behavior will become the default.
|
|
49
|
+
# config.resolve_symbolic_references_in_arrays = false
|
|
50
|
+
|
|
41
51
|
# When false, generated meta tags will be self-closing (<meta ... />) instead
|
|
42
52
|
# of open (`<meta ...>`). Default is true.
|
|
43
53
|
# config.open_meta_tags = true
|
|
@@ -15,6 +15,10 @@ module MetaTags
|
|
|
15
15
|
# A string or regexp separator to truncate text at a natural break.
|
|
16
16
|
attr_accessor :truncate_on_natural_separator
|
|
17
17
|
|
|
18
|
+
# When true, multi-item arrays stop at item boundaries instead of
|
|
19
|
+
# truncating the overflowing item.
|
|
20
|
+
attr_accessor :truncate_array_items_at_boundaries
|
|
21
|
+
|
|
18
22
|
# How many characters to truncate description to.
|
|
19
23
|
attr_accessor :description_limit
|
|
20
24
|
|
|
@@ -35,6 +39,10 @@ module MetaTags
|
|
|
35
39
|
# Default is false.
|
|
36
40
|
attr_accessor :minify_output
|
|
37
41
|
|
|
42
|
+
# When true, Symbols inside nested meta tag arrays resolve as references to
|
|
43
|
+
# normalized top-level tags. Default is false until MetaTags 3.0.
|
|
44
|
+
attr_accessor :resolve_symbolic_references_in_arrays
|
|
45
|
+
|
|
38
46
|
# Custom meta tags that should use the `property` attribute instead of `name`.
|
|
39
47
|
# An array of strings or symbols representing their names or name prefixes.
|
|
40
48
|
attr_reader :property_tags
|
|
@@ -94,6 +102,7 @@ module MetaTags
|
|
|
94
102
|
@title_limit = 70
|
|
95
103
|
@truncate_site_title_first = false
|
|
96
104
|
@truncate_on_natural_separator = " "
|
|
105
|
+
@truncate_array_items_at_boundaries = false
|
|
97
106
|
@title_tag_attributes = {}
|
|
98
107
|
@description_limit = 300
|
|
99
108
|
@keywords_limit = 255
|
|
@@ -102,6 +111,7 @@ module MetaTags
|
|
|
102
111
|
@property_tags = default_property_tags.dup
|
|
103
112
|
@open_meta_tags = true
|
|
104
113
|
@minify_output = false
|
|
114
|
+
@resolve_symbolic_references_in_arrays = false
|
|
105
115
|
@skip_canonical_links_on_noindex = false
|
|
106
116
|
end
|
|
107
117
|
end
|
|
@@ -70,13 +70,10 @@ module MetaTags
|
|
|
70
70
|
# @param defaults [Hash] list of default meta tag attributes.
|
|
71
71
|
# @return [String] page title.
|
|
72
72
|
def page_title(defaults = {})
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
full_title.presence || old_site || ""
|
|
78
|
-
ensure
|
|
79
|
-
@meta_tags[:site] = old_site if had_site
|
|
73
|
+
with_defaults(defaults) do
|
|
74
|
+
site = extract(:site)
|
|
75
|
+
extract_full_title.presence || site || ""
|
|
76
|
+
end
|
|
80
77
|
end
|
|
81
78
|
|
|
82
79
|
# Deletes and returns a meta tag value by name.
|
|
@@ -110,12 +107,19 @@ module MetaTags
|
|
|
110
107
|
#
|
|
111
108
|
# @return [Array<String>] segments of page title.
|
|
112
109
|
def extract_title
|
|
110
|
+
lowercase = extract(:lowercase) == true
|
|
113
111
|
title = extract(:title).presence
|
|
114
112
|
return [] unless title
|
|
115
113
|
|
|
116
|
-
# @type var title: Array[String]
|
|
117
114
|
title = Array(title)
|
|
118
|
-
|
|
115
|
+
if lowercase
|
|
116
|
+
return title.map do |segment|
|
|
117
|
+
string = String.try_convert(segment)
|
|
118
|
+
raise ArgumentError, "Expected a string or an object that implements #to_str" unless string
|
|
119
|
+
|
|
120
|
+
string.downcase
|
|
121
|
+
end
|
|
122
|
+
end
|
|
119
123
|
|
|
120
124
|
title
|
|
121
125
|
end
|
|
@@ -137,10 +141,43 @@ module MetaTags
|
|
|
137
141
|
TextNormalizer.safe_join([prefix, separator, suffix], "")
|
|
138
142
|
end
|
|
139
143
|
|
|
140
|
-
# Extracts
|
|
144
|
+
# Extracts robots settings as a Hash mapping tag names to rendered values.
|
|
141
145
|
#
|
|
142
|
-
# @return [Hash{String => String}]
|
|
146
|
+
# @return [Hash{String => String}] robots attributes.
|
|
143
147
|
def extract_robots
|
|
148
|
+
result = robots
|
|
149
|
+
delete(:noindex, :index, :follow, :nofollow, :noarchive)
|
|
150
|
+
[:robots, :googlebot, :bingbot].each do |bot|
|
|
151
|
+
extract(bot) if meta_tags[bot].is_a?(Hash)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
result
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns whether robots settings produce a noindex directive.
|
|
158
|
+
#
|
|
159
|
+
# @return [Boolean] true when a noindex directive will be rendered.
|
|
160
|
+
def noindex?
|
|
161
|
+
contents = robots.values
|
|
162
|
+
property_tags = MetaTags.config.property_tags.map(&:to_s)
|
|
163
|
+
[:robots, :googlebot, :bingbot].each do |bot|
|
|
164
|
+
values = meta_tags[bot]
|
|
165
|
+
next if values.is_a?(Hash) || property_tags.include?(bot.to_s)
|
|
166
|
+
|
|
167
|
+
contents.concat(Array(values))
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
contents.any? do |content|
|
|
171
|
+
content.to_s.split(",").any? { |directive| directive.strip.casecmp?("noindex") }
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
protected
|
|
176
|
+
|
|
177
|
+
# Returns robots settings without modifying the collection.
|
|
178
|
+
#
|
|
179
|
+
# @return [Hash{String => String}] robots attributes.
|
|
180
|
+
def robots
|
|
144
181
|
# @type var result: Hash[String, Array[String]]
|
|
145
182
|
result = Hash.new { |h, k| h[k] = [] }
|
|
146
183
|
|
|
@@ -154,18 +191,36 @@ module MetaTags
|
|
|
154
191
|
calculate_robots_attributes(result, attributes)
|
|
155
192
|
end
|
|
156
193
|
|
|
194
|
+
[:robots, :googlebot, :bingbot].each do |bot|
|
|
195
|
+
values = meta_tags[bot]
|
|
196
|
+
if values.is_a?(Hash)
|
|
197
|
+
values.each do |key, value|
|
|
198
|
+
next if value == false
|
|
199
|
+
|
|
200
|
+
directive = (value.nil? || value == true) ? key.to_s : "#{key}:#{value}"
|
|
201
|
+
result[bot.to_s] << directive
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
157
206
|
result.transform_values { |v| v.join(", ") }
|
|
158
207
|
end
|
|
159
208
|
|
|
160
|
-
protected
|
|
161
|
-
|
|
162
209
|
# Converts input hash to HashWithIndifferentAccess and renames :open_graph to :og.
|
|
210
|
+
# When both aliases contain hashes, they are deep-merged with :og taking precedence.
|
|
163
211
|
#
|
|
164
212
|
# @param meta_tags [Hash] list of meta tags.
|
|
165
213
|
# @return [ActiveSupport::HashWithIndifferentAccess] normalized meta tags list.
|
|
166
214
|
def normalize_open_graph(meta_tags)
|
|
167
215
|
meta_tags = meta_tags.with_indifferent_access
|
|
168
|
-
|
|
216
|
+
if meta_tags.key?(:open_graph)
|
|
217
|
+
open_graph = meta_tags.delete(:open_graph)
|
|
218
|
+
if meta_tags.key?(:og)
|
|
219
|
+
meta_tags[:og] = open_graph.dup.deep_merge!(meta_tags[:og]) if open_graph.is_a?(Hash) && meta_tags[:og].is_a?(Hash)
|
|
220
|
+
else
|
|
221
|
+
meta_tags[:og] = open_graph
|
|
222
|
+
end
|
|
223
|
+
end
|
|
169
224
|
meta_tags
|
|
170
225
|
end
|
|
171
226
|
|
|
@@ -182,15 +237,32 @@ module MetaTags
|
|
|
182
237
|
# Extracts a robots attribute name/value pair (noindex, nofollow, etc.).
|
|
183
238
|
#
|
|
184
239
|
# @param name [String, Symbol] noindex attribute name.
|
|
185
|
-
# @return [Array<
|
|
240
|
+
# @return [Array<Object>] normalized robots tag name(s) and directive value.
|
|
186
241
|
def extract_robots_attribute(name)
|
|
187
|
-
noindex =
|
|
188
|
-
noindex_name
|
|
189
|
-
|
|
242
|
+
noindex = meta_tags[name]
|
|
243
|
+
# @type var noindex_name: String | Array[String]
|
|
244
|
+
noindex_name = if noindex.is_a?(Array)
|
|
245
|
+
noindex.reject { |target| target.is_a?(String) && target.blank? }.map(&:to_s)
|
|
246
|
+
elsif noindex.is_a?(String)
|
|
247
|
+
noindex.presence || []
|
|
248
|
+
else
|
|
249
|
+
"robots"
|
|
250
|
+
end
|
|
251
|
+
noindex_value = if robots_attribute_present?(noindex) && noindex_name.present?
|
|
252
|
+
name.to_s
|
|
253
|
+
end
|
|
190
254
|
|
|
191
255
|
[noindex_name, noindex_value]
|
|
192
256
|
end
|
|
193
257
|
|
|
258
|
+
# Returns whether a robots attribute resolves to at least one target.
|
|
259
|
+
#
|
|
260
|
+
# @param value [Object] robots attribute value.
|
|
261
|
+
# @return [Boolean] true when the attribute produces a directive.
|
|
262
|
+
def robots_attribute_present?(value)
|
|
263
|
+
!!value && (!value.is_a?(Array) || !value.empty?)
|
|
264
|
+
end
|
|
265
|
+
|
|
194
266
|
# Appends resolved robots directives while preserving first-write priority.
|
|
195
267
|
#
|
|
196
268
|
# @param result [Hash{String => Array<String>}] robots directives grouped by tag name.
|
|
@@ -203,8 +275,8 @@ module MetaTags
|
|
|
203
275
|
names, value = extract_robots_attribute(attribute)
|
|
204
276
|
next unless value
|
|
205
277
|
|
|
206
|
-
|
|
207
|
-
|
|
278
|
+
robot_names = names.is_a?(String) ? [names] : names
|
|
279
|
+
robot_names.each do |name|
|
|
208
280
|
apply_robots_value(result, name, value, processed)
|
|
209
281
|
end
|
|
210
282
|
end
|
|
@@ -213,7 +285,7 @@ module MetaTags
|
|
|
213
285
|
# Records a robots directive unless it has already been set for the same key.
|
|
214
286
|
#
|
|
215
287
|
# @param result [Hash{String => Array<String>}] robots directives grouped by tag name.
|
|
216
|
-
# @param name [String
|
|
288
|
+
# @param name [String] robots tag name.
|
|
217
289
|
# @param value [String] robots directive value.
|
|
218
290
|
# @param processed [Set<String>] names already recorded in this pass.
|
|
219
291
|
# @return [void]
|
data/lib/meta_tags/railtie.rb
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
module MetaTags
|
|
4
4
|
# Hooks MetaTags helpers into Rails controllers and views.
|
|
5
5
|
class Railtie < Rails::Railtie
|
|
6
|
+
initializer "meta_tags.register_deprecator", before: :load_environment_config do |app|
|
|
7
|
+
app.deprecators[:meta_tags] = MetaTags.deprecator if app.respond_to?(:deprecators)
|
|
8
|
+
end
|
|
9
|
+
|
|
6
10
|
initializer "meta_tags.setup_action_controller" do
|
|
7
11
|
ActiveSupport.on_load :action_controller do
|
|
8
12
|
include MetaTags::ControllerHelper
|
data/lib/meta_tags/renderer.rb
CHANGED
|
@@ -77,7 +77,7 @@ module MetaTags
|
|
|
77
77
|
|
|
78
78
|
icon.each do |icon_params|
|
|
79
79
|
icon_params = {rel: "icon", type: "image/x-icon"}.with_indifferent_access.merge(icon_params)
|
|
80
|
-
tags << Tag.new(:link, icon_params)
|
|
80
|
+
tags << Tag.new(:link, icon_params) if icon_params[:href].present?
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -123,7 +123,8 @@ module MetaTags
|
|
|
123
123
|
end
|
|
124
124
|
elsif alternate.is_a?(Array)
|
|
125
125
|
alternate.each do |link_params|
|
|
126
|
-
|
|
126
|
+
link_params = {rel: "alternate"}.with_indifferent_access.merge(link_params)
|
|
127
|
+
tags << Tag.new(:link, link_params) if link_params[:href].present?
|
|
127
128
|
end
|
|
128
129
|
end
|
|
129
130
|
end
|
|
@@ -160,7 +161,7 @@ module MetaTags
|
|
|
160
161
|
# @param tags [Array<Tag>] a buffer object to store tags in.
|
|
161
162
|
def render_canonical_link(tags)
|
|
162
163
|
href = meta_tags.extract(:canonical) # extract, so it's not used anywhere else
|
|
163
|
-
return if MetaTags.config.skip_canonical_links_on_noindex && meta_tags
|
|
164
|
+
return if MetaTags.config.skip_canonical_links_on_noindex && meta_tags.noindex?
|
|
164
165
|
return if href.blank?
|
|
165
166
|
|
|
166
167
|
@normalized_meta_tags[:canonical] = href
|
|
@@ -214,7 +215,11 @@ module MetaTags
|
|
|
214
215
|
when Array
|
|
215
216
|
process_array(tags, property, content, itemprop: itemprop)
|
|
216
217
|
else
|
|
217
|
-
|
|
218
|
+
if content.is_a?(Symbol) && MetaTags.config.resolve_symbolic_references_in_arrays
|
|
219
|
+
process_reference(tags, property, content, itemprop: itemprop)
|
|
220
|
+
else
|
|
221
|
+
render_tag(tags, property, content, itemprop: itemprop)
|
|
222
|
+
end
|
|
218
223
|
end
|
|
219
224
|
end
|
|
220
225
|
|
|
@@ -225,8 +230,12 @@ module MetaTags
|
|
|
225
230
|
# @param content [Hash] nested meta tag attributes.
|
|
226
231
|
# @param itemprop [String, Symbol, nil] inherited itemprop value.
|
|
227
232
|
def process_hash(tags, property, content, itemprop: nil)
|
|
228
|
-
|
|
233
|
+
itemprop_key = content.key?(:itemprop) ? :itemprop : "itemprop"
|
|
234
|
+
current_itemprop = content.key?(itemprop_key) ? content[itemprop_key] : itemprop
|
|
235
|
+
|
|
229
236
|
content.each do |key, value|
|
|
237
|
+
next if key.to_s == "itemprop"
|
|
238
|
+
|
|
230
239
|
if key.to_s == "_"
|
|
231
240
|
key = property
|
|
232
241
|
next_itemprop = current_itemprop
|
|
@@ -235,12 +244,11 @@ module MetaTags
|
|
|
235
244
|
next_itemprop = nil
|
|
236
245
|
end
|
|
237
246
|
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
if value.is_a?(Symbol)
|
|
248
|
+
process_reference(tags, key, value, itemprop: next_itemprop)
|
|
240
249
|
else
|
|
241
|
-
value
|
|
250
|
+
process_tree(tags, key, value, itemprop: next_itemprop)
|
|
242
251
|
end
|
|
243
|
-
process_tree(tags, key, normalized_value, itemprop: next_itemprop)
|
|
244
252
|
end
|
|
245
253
|
end
|
|
246
254
|
|
|
@@ -251,9 +259,39 @@ module MetaTags
|
|
|
251
259
|
# @param content [Array] array of nested meta tag attributes or values.
|
|
252
260
|
# @param itemprop [String, Symbol, nil] value of the itemprop attribute.
|
|
253
261
|
def process_array(tags, property, content, itemprop: nil)
|
|
262
|
+
unless MetaTags.config.resolve_symbolic_references_in_arrays
|
|
263
|
+
symbol_reference = content.find { |value| value.is_a?(Symbol) }
|
|
264
|
+
if symbol_reference
|
|
265
|
+
MetaTags.deprecator.warn(
|
|
266
|
+
"Passing #{symbol_reference.inspect} directly inside a nested meta tag Array is deprecated. " \
|
|
267
|
+
"It is rendered literally in MetaTags 2.x but will be interpreted as a mirrored reference in MetaTags 3.0. " \
|
|
268
|
+
"Set MetaTags.config.resolve_symbolic_references_in_arrays = true to opt in now; this behavior will " \
|
|
269
|
+
"become the default in MetaTags 3.0. Use a String to keep a literal value."
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
254
274
|
content.each { |value| process_tree(tags, property, value, itemprop: itemprop) }
|
|
255
275
|
end
|
|
256
276
|
|
|
277
|
+
# Resolves a Symbol reference and processes its normalized value once.
|
|
278
|
+
#
|
|
279
|
+
# @param tags [Array<Tag>] a buffer object to store tags in.
|
|
280
|
+
# @param property [String, Symbol] the meta tag property name.
|
|
281
|
+
# @param reference [Symbol] normalized top-level meta tag reference.
|
|
282
|
+
# @param itemprop [String, Symbol, nil] value of the itemprop attribute.
|
|
283
|
+
def process_reference(tags, property, reference, itemprop: nil)
|
|
284
|
+
content = normalized_meta_tags[reference]
|
|
285
|
+
case content
|
|
286
|
+
when Hash
|
|
287
|
+
process_hash(tags, property, content, itemprop: itemprop)
|
|
288
|
+
when Array
|
|
289
|
+
process_array(tags, property, content, itemprop: itemprop)
|
|
290
|
+
else
|
|
291
|
+
render_tag(tags, property, content, itemprop: itemprop)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
257
295
|
# Renders a single meta tag.
|
|
258
296
|
#
|
|
259
297
|
# @param tags [Array<Tag>] a buffer object to store tags in.
|
|
@@ -152,13 +152,14 @@ module MetaTags
|
|
|
152
152
|
return string_array if limit.nil? || limit <= 0
|
|
153
153
|
|
|
154
154
|
length = 0
|
|
155
|
+
preserve_items = string_array.size > 1 && MetaTags.config.truncate_array_items_at_boundaries
|
|
155
156
|
result = [] # : Array[String]
|
|
156
157
|
|
|
157
158
|
string_array.each do |string|
|
|
158
159
|
limit_left = calculate_limit_left(limit, length, result, separator)
|
|
159
160
|
|
|
160
161
|
if string.length > limit_left
|
|
161
|
-
result << truncate(string, limit_left)
|
|
162
|
+
result << truncate(string, limit_left) if !preserve_items || result.empty?
|
|
162
163
|
break string_array
|
|
163
164
|
end
|
|
164
165
|
|
data/lib/meta_tags/version.rb
CHANGED
|
@@ -151,6 +151,9 @@ module MetaTags
|
|
|
151
151
|
# @option default [String, Integer] :refresh (nil) meta refresh tag;
|
|
152
152
|
# @option default [Hash] :open_graph ({}) add Open Graph meta tags.
|
|
153
153
|
# @option default [Hash] :open_search ({}) add Open Search link tag.
|
|
154
|
+
# @option default [Hash] :robots ({}) add robots meta tags.
|
|
155
|
+
# @option default [Hash] :googlebot ({}) add googlebot meta tags.
|
|
156
|
+
# @option default [Hash] :bingbot ({}) add bingbot meta tags.
|
|
154
157
|
# @return [String] HTML meta tags to render in HEAD section of the
|
|
155
158
|
# HTML document.
|
|
156
159
|
# @example Render meta tags in a layout
|
data/lib/meta_tags.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "set"
|
|
4
4
|
require "active_support/core_ext/hash/indifferent_access"
|
|
5
|
+
require "active_support/deprecation"
|
|
5
6
|
|
|
6
7
|
# MetaTags gem namespace.
|
|
7
8
|
module MetaTags
|
|
@@ -10,6 +11,11 @@ module MetaTags
|
|
|
10
11
|
@config ||= Configuration.new
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
# Returns the deprecator used for MetaTags API warnings.
|
|
15
|
+
def self.deprecator
|
|
16
|
+
@deprecator ||= ActiveSupport::Deprecation.new("3.0", "MetaTags")
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
# Configures MetaTags gem.
|
|
14
20
|
#
|
|
15
21
|
# @yield [Configuration] configuration object.
|
|
@@ -33,6 +33,11 @@ module ActionController
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
module ActiveSupport
|
|
36
|
+
class Deprecation
|
|
37
|
+
def initialize: (String deprecation_horizon, String gem_name) -> void
|
|
38
|
+
def warn: (String message) -> void
|
|
39
|
+
end
|
|
40
|
+
|
|
36
41
|
class HashWithIndifferentAccess[unchecked out K, unchecked out V] < Hash[K, V]
|
|
37
42
|
def with_indifferent_access: () -> self
|
|
38
43
|
def deep_merge!: (instance | Hash[K, V] other) -> self
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
module MetaTags
|
|
2
2
|
class Configuration
|
|
3
3
|
type html_tag_key = String | Symbol
|
|
4
|
-
type html_tag_value = Hash[html_tag_key, html_tag_value] | html_tag_content
|
|
5
|
-
type html_tag_content = String? | Symbol | (_Stringish & Object)
|
|
4
|
+
type html_tag_value = Hash[html_tag_key, html_tag_value] | Array[html_tag_value] | html_tag_content
|
|
5
|
+
type html_tag_content = String? | Symbol | Numeric | bool | (_Stringish & Object)
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
attr_accessor title_limit: Integer?
|
|
9
9
|
attr_accessor truncate_site_title_first: bool
|
|
10
10
|
attr_accessor truncate_on_natural_separator: String?|Regexp
|
|
11
|
+
attr_accessor truncate_array_items_at_boundaries: bool
|
|
11
12
|
attr_accessor title_tag_attributes: Hash[html_tag_key, html_tag_value]?
|
|
12
|
-
attr_accessor description_limit: Integer
|
|
13
|
+
attr_accessor description_limit: Integer?
|
|
13
14
|
attr_accessor keywords_limit: Integer
|
|
14
15
|
attr_accessor keywords_separator: String
|
|
15
16
|
attr_accessor keywords_lowercase: bool
|
|
16
17
|
attr_accessor open_meta_tags: bool
|
|
17
18
|
attr_accessor minify_output: bool
|
|
19
|
+
attr_accessor resolve_symbolic_references_in_arrays: bool
|
|
18
20
|
attr_reader property_tags: Array[String | Symbol]
|
|
19
21
|
attr_accessor skip_canonical_links_on_noindex: bool
|
|
20
22
|
|
|
@@ -28,14 +28,20 @@ module MetaTags
|
|
|
28
28
|
|
|
29
29
|
def extract_robots: () -> Hash[String, String]
|
|
30
30
|
|
|
31
|
+
def noindex?: () -> bool
|
|
32
|
+
|
|
33
|
+
def robots: () -> Hash[String, String]
|
|
34
|
+
|
|
31
35
|
def normalize_open_graph: (Hash[String | Symbol, untyped] meta_tags) -> ActiveSupport::HashWithIndifferentAccess[String | Symbol, untyped]
|
|
32
36
|
|
|
33
37
|
def extract_separator_section: (String | Symbol name, String default) -> String
|
|
34
38
|
|
|
35
|
-
def extract_robots_attribute: (String | Symbol name) -> [String | Array[String
|
|
39
|
+
def extract_robots_attribute: (String | Symbol name) -> [String | Array[String], String?]
|
|
40
|
+
|
|
41
|
+
def robots_attribute_present?: (untyped value) -> bool
|
|
36
42
|
|
|
37
43
|
def calculate_robots_attributes: (Hash[String, Array[String]] result, String | Symbol | Array[String | Symbol] attributes) -> void
|
|
38
44
|
|
|
39
|
-
def apply_robots_value: (Hash[String, Array[String]] result, String
|
|
45
|
+
def apply_robots_value: (Hash[String, Array[String]] result, String name, String value, Set[String] processed) -> void
|
|
40
46
|
end
|
|
41
47
|
end
|
|
@@ -2,7 +2,7 @@ module MetaTags
|
|
|
2
2
|
class Renderer
|
|
3
3
|
type meta_key = String | Symbol
|
|
4
4
|
type meta_value = Hash[meta_key, meta_value] | Array[meta_value] | meta_content
|
|
5
|
-
type meta_content = String? | Symbol | Integer | bool | (_Timish & Object) | (_Stringish & Object)
|
|
5
|
+
type meta_content = String? | Symbol | Integer | Float | bool | (_Timish & Object) | (_Stringish & Object)
|
|
6
6
|
|
|
7
7
|
attr_reader meta_tags: MetaTagsCollection
|
|
8
8
|
attr_reader normalized_meta_tags: Hash[Symbol, meta_value]
|
|
@@ -31,20 +31,24 @@ module MetaTags
|
|
|
31
31
|
|
|
32
32
|
def render_canonical_link: (Array[Tag] tags) -> void
|
|
33
33
|
|
|
34
|
-
def render_hashes: (Array[Tag] tags
|
|
34
|
+
def render_hashes: (Array[Tag] tags) -> void
|
|
35
35
|
|
|
36
|
-
def render_hash: (Array[Tag] tags,
|
|
36
|
+
def render_hash: (Array[Tag] tags, meta_key key) -> void
|
|
37
37
|
|
|
38
38
|
def render_custom: (Array[Tag] tags) -> void
|
|
39
39
|
|
|
40
|
-
def process_tree: (Array[Tag] tags, meta_key property, meta_value content, ?itemprop:
|
|
40
|
+
def process_tree: (Array[Tag] tags, meta_key property, meta_value content, ?itemprop: meta_value itemprop) -> void
|
|
41
41
|
|
|
42
|
-
def process_hash: (Array[Tag] tags, meta_key property, Hash[meta_key, meta_value] content,
|
|
42
|
+
def process_hash: (Array[Tag] tags, meta_key property, Hash[meta_key, meta_value] content, ?itemprop: meta_value itemprop) -> void
|
|
43
43
|
|
|
44
|
-
def process_array: (Array[Tag] tags, meta_key property, Array[meta_value] content,
|
|
44
|
+
def process_array: (Array[Tag] tags, meta_key property, Array[meta_value] content, ?itemprop: meta_value itemprop) -> void
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def process_reference: (Array[Tag] tags, meta_key property, Symbol reference, ?itemprop: meta_value itemprop) -> void
|
|
47
|
+
|
|
48
|
+
def render_tag: (Array[Tag] tags, meta_key name, meta_content value, ?itemprop: meta_value itemprop) -> void
|
|
47
49
|
|
|
48
50
|
def configured_name_key: (meta_key name) -> Symbol
|
|
51
|
+
|
|
52
|
+
def property_tag?: (String name, String tag_name) -> bool
|
|
49
53
|
end
|
|
50
54
|
end
|
data/sig/lib/meta_tags/tag.rbs
CHANGED
|
@@ -7,6 +7,6 @@ module MetaTags
|
|
|
7
7
|
|
|
8
8
|
def render: (_ActionViewBase view) -> String
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def serialize_iso8601_attributes: (Hash[String | Symbol, untyped] attributes) -> Hash[String | Symbol, untyped]
|
|
11
11
|
end
|
|
12
12
|
end
|
data/sig/lib/meta_tags.rbs
CHANGED
|
@@ -2,8 +2,10 @@ module MetaTags
|
|
|
2
2
|
type meta_tags = Hash[Renderer::meta_key, Renderer::meta_value]
|
|
3
3
|
|
|
4
4
|
self.@config: Configuration
|
|
5
|
+
self.@deprecator: ActiveSupport::Deprecation?
|
|
5
6
|
|
|
6
7
|
def self.config: () -> Configuration
|
|
8
|
+
def self.deprecator: () -> ActiveSupport::Deprecation
|
|
7
9
|
def self.configure: () { (Configuration) -> void } -> void
|
|
8
10
|
|
|
9
11
|
interface _Stringish
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: meta-tags
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.24.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmytro Shteflyuk
|
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
|
30
30
|
pPzkBOwQl2p1ykAEUe7cwoqJcO+1rPeTHjudNqhOfc3VnMA8A7uXOkTtYNVhvApn
|
|
31
31
|
oW4hE/8SnZkQm8jzdi0iX9nWxo8=
|
|
32
32
|
-----END CERTIFICATE-----
|
|
33
|
-
date: 2026-
|
|
33
|
+
date: 2026-09-01 00:00:00.000000000 Z
|
|
34
34
|
dependencies:
|
|
35
35
|
- !ruby/object:Gem::Dependency
|
|
36
36
|
name: actionpack
|
|
@@ -227,6 +227,7 @@ files:
|
|
|
227
227
|
- CONTRIBUTING.md
|
|
228
228
|
- MIT-LICENSE
|
|
229
229
|
- README.md
|
|
230
|
+
- SECURITY.md
|
|
230
231
|
- certs/kpumuk.pem
|
|
231
232
|
- lib/generators/meta_tags/install_generator.rb
|
|
232
233
|
- lib/generators/meta_tags/templates/config/initializers/meta_tags.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|