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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b4bb486b4a8b270f4e782ad51e97e5df1e48c494226f28c4242aaf17b4a20a3
4
- data.tar.gz: 43ff472ad6406ee187d1234eb363d6a9a9d9360c460b651bfcf00ab503a62d01
3
+ metadata.gz: 566365edffbf0315633065a07584898e708104ed5657ae1b673302dcd10ed3b0
4
+ data.tar.gz: ffde1c27ef6745ad5ce3635118a63e4af9b3845dc4ee6ef4eb1c2747bd93f7e5
5
5
  SHA512:
6
- metadata.gz: 98406f57fb0efc987f850478b5178c95634785853fdda24898aecde45b40893920c3533b86627d12e8a5b2cdd7526fb3a7c15b44617087c2029cb2329dac2908
7
- data.tar.gz: dde7d53752a74f005e5ddafafea009c17aad3e67f119e825f5478285421c041060b7b766d7bcdb04bd4fb1bdea0d892a59b8a84e9c348ef55786b90b12c9a189
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
  [![Tests](https://github.com/kpumuk/meta-tags/actions/workflows/tests.yml/badge.svg)](https://github.com/kpumuk/meta-tags/actions/workflows/tests.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/meta-tags.svg)](https://badge.fury.io/rb/meta-tags)
5
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/kpumuk/meta-tags/badge)](https://scorecard.dev/viewer/?uri=github.com/kpumuk/meta-tags)
5
6
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
6
7
  [![Gem Downloads](https://img.shields.io/gem/dt/meta-tags.svg)](https://badge.fury.io/rb/meta-tags)
7
8
  [![Changelog](https://img.shields.io/badge/Changelog-latest-blue.svg)](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 title format:
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"](http://www.google.com/support/webmasters/bin/answer.py?answer=96569)
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" href="/favicon.ico" type="image/x-icon">
432
- set_meta_tags icon: "/favicon.png", type: "image/png"
433
- # <link rel="icon" href="/favicon.png" type="image/png">
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" type="image/png" sizes="32x32 96x96">
439
- # <link rel="apple-touch-icon-precomposed" href="/images/icons/icon_itouch_precomp_32.png" type="image/png" sizes="32x32">
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](http://support.google.com/webmasters/bin/answer.py?hl=en&answer=1663744)
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: url_for(format: :amp, only_path: false)
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 content="5" http-equiv="refresh">
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" http-equiv="refresh">
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 href="/opensearch.xml" rel="search" title="Open Search" type="application/opensearchdescription+xml">
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](http://www.opensearch.org/Specifications/OpenSearch/1.1)
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 property="foo:bar" content="lorem">
572
- # <meta property="foo:baz:qux" content="ipsum">
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: "view-source:http://examples.opengraphprotocol.us/image-array.html",
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
- had_site = @meta_tags.key?(:site)
74
- old_site = @meta_tags[:site]
75
- @meta_tags[:site] = nil if had_site
76
- full_title = with_defaults(defaults) { extract_full_title }
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
- return title.map(&:downcase) if extract(:lowercase) == true
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 noindex settings as a Hash mapping noindex tag name to value.
144
+ # Extracts robots settings as a Hash mapping tag names to rendered values.
141
145
  #
142
- # @return [Hash{String => String}] noindex attributes.
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
- meta_tags[:og] = meta_tags.delete(:open_graph) if meta_tags.key?(:open_graph)
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<String>] noindex attribute name and value pair.
240
+ # @return [Array<Object>] normalized robots tag name(s) and directive value.
186
241
  def extract_robots_attribute(name)
187
- noindex = extract(name)
188
- noindex_name = (noindex.is_a?(String) || noindex.is_a?(Array)) ? noindex : "robots"
189
- noindex_value = noindex ? name.to_s : nil
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
- Array(names).each do |name|
207
- # @type var name: String | Symbol
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, Symbol] robots tag name.
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]
@@ -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
@@ -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
- tags << Tag.new(:link, {rel: "alternate"}.with_indifferent_access.merge(link_params))
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[:noindex]
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
- render_tag(tags, property, content, itemprop: itemprop)
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
- current_itemprop = content.delete(:itemprop)
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
- normalized_value = if value.is_a?(Symbol)
239
- normalized_meta_tags[value]
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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MetaTags
4
4
  # Gem version.
5
- VERSION = "2.23.0"
5
+ VERSION = "2.24.0"
6
6
  public_constant :VERSION
7
7
  end
@@ -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 | Symbol], 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 | Symbol name, String value, Set[String] processed) -> void
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, **untyped opts) -> void
34
+ def render_hashes: (Array[Tag] tags) -> void
35
35
 
36
- def render_hash: (Array[Tag] tags, untyped key, **untyped opts) -> void
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: meta_key? itemprop, **untyped opts) -> void
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, **untyped opts) -> void
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, **untyped opts) -> void
44
+ def process_array: (Array[Tag] tags, meta_key property, Array[meta_value] content, ?itemprop: meta_value itemprop) -> void
45
45
 
46
- def render_tag: (Array[Tag] tags, meta_key name, meta_content value, ?itemprop: meta_key? itemprop) -> void
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
@@ -7,6 +7,6 @@ module MetaTags
7
7
 
8
8
  def render: (_ActionViewBase view) -> String
9
9
 
10
- def prepare_attributes: (Hash[String | Symbol, untyped] attributes) -> Hash[String | Symbol, untyped]
10
+ def serialize_iso8601_attributes: (Hash[String | Symbol, untyped] attributes) -> Hash[String | Symbol, untyped]
11
11
  end
12
12
  end
@@ -4,6 +4,7 @@ module MetaTags
4
4
 
5
5
  type stringish = String | (_Stringish & Object)
6
6
  type robots_value = bool | String | Array[String]
7
+ type meta_tags = Hash[Renderer::meta_key, Renderer::meta_value]
7
8
 
8
9
  def meta_tags: () -> MetaTagsCollection
9
10
 
@@ -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.23.0
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-03-16 00:00:00.000000000 Z
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