page_structured_data 1.0.14 → 1.0.15
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
- data/CHANGELOG.md +15 -0
- data/README.md +24 -16
- data/Rakefile +5 -0
- data/app/src/page_structured_data/breadcrumbs.rb +4 -5
- data/app/src/page_structured_data/json_ld.rb +22 -0
- data/app/src/page_structured_data/page.rb +8 -3
- data/app/src/page_structured_data/page_types/article.rb +1 -5
- data/app/src/page_structured_data/page_types/discussion_forum_posting.rb +17 -0
- data/app/src/page_structured_data/page_types/organization.rb +1 -5
- data/app/src/page_structured_data/page_types/web_site.rb +1 -5
- data/app/views/page_structured_data/_meta_tags.html.erb +5 -4
- data/lib/page_structured_data/version.rb +1 -1
- data/lib/page_structured_data.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f455392a943824c8c84fbeb1a95e0250d9f959f98c8e5361c5d57d1f38140d91
|
|
4
|
+
data.tar.gz: 306f4a1246e196db8a7e58bb89aa1a73454c3fc873c3431818a1c0b2cdb5bc93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a0261a5b6a280806f36fccea7676b6f0656e0fab4f0ab3f13087984923f904b0895a88921527783bdbecaa30c3f653ee16ee8e1fd4dfdc88a294047203fa093
|
|
7
|
+
data.tar.gz: f8cc5db7758b6170705eb55fa53208d1b38f5cf69343e734c6e62ded077916cc7d74ff7d8a54cbff7ecbed287cbe2f8fc5aaab77c0f425abcf97610837075872
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 1.0.15 - 2026-08-01
|
|
8
|
+
|
|
9
|
+
- Add optional page-level `social_description` support for distinct Open Graph
|
|
10
|
+
and X/Twitter descriptions with fallback to the standard SEO description.
|
|
11
|
+
- Escape JSON-LD independently of host-app Active Support configuration before
|
|
12
|
+
rendering script tags as HTML-safe.
|
|
13
|
+
- Stop rendering invalid current-page-only breadcrumb JSON-LD while preserving
|
|
14
|
+
explicit breadcrumb hierarchies.
|
|
15
|
+
- Render Google-compatible `text` for discussion forum posts while retaining
|
|
16
|
+
`articleBody` for backward compatibility, and warn when author or post
|
|
17
|
+
content is missing.
|
|
18
|
+
- Ensure the documented `rake test` command executes the complete Rails test
|
|
19
|
+
suite and constrain test dependencies to compatible Minitest and Rails 7.0
|
|
20
|
+
SQLite lines.
|
|
21
|
+
|
|
7
22
|
## 1.0.14 - 2026-07-05
|
|
8
23
|
|
|
9
24
|
- Add page-level `robots` meta tag support.
|
data/README.md
CHANGED
|
@@ -51,15 +51,12 @@ Configure application-wide defaults in an initializer:
|
|
|
51
51
|
Rails.application.config.after_initialize do
|
|
52
52
|
PageStructuredData.config do |config|
|
|
53
53
|
config.base_app_name = "AwesomestApp"
|
|
54
|
-
config.render_default_breadcrumb_json_ld = true
|
|
55
54
|
end
|
|
56
55
|
end
|
|
57
56
|
```
|
|
58
57
|
|
|
59
58
|
`base_app_name` is appended to generated page titles.
|
|
60
59
|
|
|
61
|
-
`render_default_breadcrumb_json_ld` controls whether pages without an explicit breadcrumb render current-page-only breadcrumb JSON-LD. It defaults to `true` for backward compatibility. Set it to `false` if you only want breadcrumb JSON-LD when a `PageStructuredData::Breadcrumbs` object is passed to the page.
|
|
62
|
-
|
|
63
60
|
For example:
|
|
64
61
|
|
|
65
62
|
```ruby
|
|
@@ -98,6 +95,7 @@ Set `@page_meta` in the controller or view before the layout renders:
|
|
|
98
95
|
title: "Home",
|
|
99
96
|
extra_title: "Official Page",
|
|
100
97
|
description: "Welcome to my page",
|
|
98
|
+
social_description: "See what is new on my page",
|
|
101
99
|
image: image_url("social/home.png"),
|
|
102
100
|
canonical_url: home_url,
|
|
103
101
|
fallback_image: image_url("social/default.png"),
|
|
@@ -105,6 +103,12 @@ Set `@page_meta` in the controller or view before the layout renders:
|
|
|
105
103
|
)
|
|
106
104
|
```
|
|
107
105
|
|
|
106
|
+
`description` renders the standard SEO description. `social_description` is
|
|
107
|
+
optional and renders the Open Graph and X/Twitter descriptions when it is
|
|
108
|
+
nonblank. When `social_description` is nil or blank, those social tags fall
|
|
109
|
+
back to `description`. This allows intentional social copy without changing the
|
|
110
|
+
standard SEO description.
|
|
111
|
+
|
|
108
112
|
The generated title is built from:
|
|
109
113
|
|
|
110
114
|
1. `title`
|
|
@@ -139,7 +143,11 @@ Pass the breadcrumbs into the page object:
|
|
|
139
143
|
|
|
140
144
|
This renders `BreadcrumbList` JSON-LD similar to Google's breadcrumb structured data format.
|
|
141
145
|
|
|
142
|
-
|
|
146
|
+
Breadcrumb JSON-LD renders only when the resulting list contains at least two
|
|
147
|
+
items. Pages without a breadcrumb hierarchy do not emit a current-page-only
|
|
148
|
+
list because Google does not accept one-item breadcrumb data. The existing
|
|
149
|
+
`render_default_breadcrumb_json_ld` configuration remains accepted for
|
|
150
|
+
compatibility, but it cannot make an invalid one-item list render.
|
|
143
151
|
|
|
144
152
|
## Structured Page Types
|
|
145
153
|
|
|
@@ -378,6 +386,8 @@ post_page_type = PageStructuredData::PageTypes::DiscussionForumPosting.new(
|
|
|
378
386
|
```
|
|
379
387
|
|
|
380
388
|
Only pass engagement counts that are public and visible on the rendered page.
|
|
389
|
+
`DiscussionForumPosting` renders the supplied `text` as both `text` and the
|
|
390
|
+
backward-compatible `articleBody` property.
|
|
381
391
|
|
|
382
392
|
### Page-Local Site Name
|
|
383
393
|
|
|
@@ -401,7 +411,7 @@ PageStructuredData::Page.new(
|
|
|
401
411
|
|
|
402
412
|
### Breadcrumb JSON-LD Control
|
|
403
413
|
|
|
404
|
-
|
|
414
|
+
Suppress an explicit breadcrumb hierarchy for one page:
|
|
405
415
|
|
|
406
416
|
```ruby
|
|
407
417
|
PageStructuredData::Page.new(
|
|
@@ -410,16 +420,9 @@ PageStructuredData::Page.new(
|
|
|
410
420
|
)
|
|
411
421
|
```
|
|
412
422
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
PageStructuredData.render_default_breadcrumb_json_ld = false
|
|
417
|
-
|
|
418
|
-
PageStructuredData::Page.new(
|
|
419
|
-
title: "Standalone Page",
|
|
420
|
-
render_breadcrumb_json_ld: true
|
|
421
|
-
)
|
|
422
|
-
```
|
|
423
|
+
Pages need at least one hierarchy item plus the current page before breadcrumb
|
|
424
|
+
JSON-LD renders. Setting `render_breadcrumb_json_ld: true` does not synthesize
|
|
425
|
+
missing hierarchy data.
|
|
423
426
|
|
|
424
427
|
### Paginated Archive
|
|
425
428
|
|
|
@@ -452,6 +455,7 @@ PageStructuredData::Page.new(
|
|
|
452
455
|
PageStructuredData::Page.new(
|
|
453
456
|
title:,
|
|
454
457
|
description: nil,
|
|
458
|
+
social_description: nil,
|
|
455
459
|
image: nil,
|
|
456
460
|
extra_title: "",
|
|
457
461
|
breadcrumb: nil,
|
|
@@ -465,8 +469,11 @@ PageStructuredData::Page.new(
|
|
|
465
469
|
)
|
|
466
470
|
```
|
|
467
471
|
|
|
472
|
+
`social_description` overrides `description` for Open Graph and X/Twitter
|
|
473
|
+
description tags when nonblank. It does not change the standard SEO description
|
|
474
|
+
tag.
|
|
468
475
|
`base_app_name` overrides `PageStructuredData.base_app_name` for one page. Pass an empty string to suppress the global app name for a specific page.
|
|
469
|
-
`render_breadcrumb_json_ld` can be set to `
|
|
476
|
+
`render_breadcrumb_json_ld` can be set to `false` to suppress an explicit breadcrumb object. A true value never renders a breadcrumb list with fewer than two items.
|
|
470
477
|
`robots` can be a string or array and renders a `<meta name="robots">` tag when present.
|
|
471
478
|
|
|
472
479
|
Important methods:
|
|
@@ -474,6 +481,7 @@ Important methods:
|
|
|
474
481
|
- `page_title`: returns the composed page title.
|
|
475
482
|
- `json_lds`: returns the JSON-LD script tags for breadcrumbs and page type data.
|
|
476
483
|
- `resolved_image`: returns `image` or `fallback_image`.
|
|
484
|
+
- `resolved_social_description`: returns a nonblank `social_description` or falls back to `description`.
|
|
477
485
|
- `robots_content`: returns the rendered robots directives.
|
|
478
486
|
- `warnings`: returns soft validation warnings for the page and page types.
|
|
479
487
|
- `valid?`: returns `true` when `warnings` is empty.
|
data/Rakefile
CHANGED
|
@@ -22,11 +22,10 @@ module PageStructuredData
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def json_ld(current_page_title:)
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
25
|
+
node = to_h(current_page_title: current_page_title)
|
|
26
|
+
return if node[:itemListElement].size < 2
|
|
27
|
+
|
|
28
|
+
JsonLd.script_tag(node)
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
private
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/string/output_safety'
|
|
4
|
+
|
|
5
|
+
module PageStructuredData
|
|
6
|
+
# Builds JSON-LD script tags with escaping that does not depend on host-app
|
|
7
|
+
# Active Support configuration.
|
|
8
|
+
module JsonLd
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def script_tag(node)
|
|
12
|
+
json = ActiveSupport::JSON.encode(node)
|
|
13
|
+
escaped_json = ERB::Util.json_escape(json)
|
|
14
|
+
|
|
15
|
+
%(
|
|
16
|
+
<script type="application/ld+json">
|
|
17
|
+
#{escaped_json}
|
|
18
|
+
</script>
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
module PageStructuredData
|
|
4
4
|
# Basic page metadata for any page
|
|
5
5
|
class Page
|
|
6
|
-
attr_reader :title, :description, :image, :extra_title, :breadcrumb, :page_type, :page_types,
|
|
7
|
-
:fallback_image, :base_app_name, :render_breadcrumb_json_ld, :robots
|
|
6
|
+
attr_reader :title, :description, :social_description, :image, :extra_title, :breadcrumb, :page_type, :page_types,
|
|
7
|
+
:canonical_url, :fallback_image, :base_app_name, :render_breadcrumb_json_ld, :robots
|
|
8
8
|
|
|
9
|
-
def initialize(title:, description: nil, image: nil, # rubocop:disable Metrics/ParameterLists
|
|
9
|
+
def initialize(title:, description: nil, social_description: nil, image: nil, # rubocop:disable Metrics/ParameterLists
|
|
10
10
|
extra_title: '', breadcrumb: nil, page_type: nil, page_types: nil, canonical_url: nil,
|
|
11
11
|
fallback_image: nil, base_app_name: nil, render_breadcrumb_json_ld: nil, robots: nil)
|
|
12
12
|
@title = title
|
|
13
13
|
@description = description
|
|
14
|
+
@social_description = social_description
|
|
14
15
|
@image = image
|
|
15
16
|
@extra_title = extra_title
|
|
16
17
|
@breadcrumb = breadcrumb
|
|
@@ -48,6 +49,10 @@ module PageStructuredData
|
|
|
48
49
|
image || fallback_image
|
|
49
50
|
end
|
|
50
51
|
|
|
52
|
+
def resolved_social_description
|
|
53
|
+
social_description.presence || description
|
|
54
|
+
end
|
|
55
|
+
|
|
51
56
|
def robots_content
|
|
52
57
|
Array(robots).compact.join(',')
|
|
53
58
|
end
|
|
@@ -4,11 +4,28 @@ module PageStructuredData
|
|
|
4
4
|
module PageTypes
|
|
5
5
|
# schema.org structured data for discussion forum posts.
|
|
6
6
|
class DiscussionForumPosting < Article
|
|
7
|
+
def to_h
|
|
8
|
+
super.tap do |node|
|
|
9
|
+
node[:text] = article_body if article_body.present?
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def warnings
|
|
14
|
+
super + discussion_forum_warnings
|
|
15
|
+
end
|
|
16
|
+
|
|
7
17
|
private
|
|
8
18
|
|
|
9
19
|
def schema_type
|
|
10
20
|
'DiscussionForumPosting'
|
|
11
21
|
end
|
|
22
|
+
|
|
23
|
+
def discussion_forum_warnings
|
|
24
|
+
warnings = []
|
|
25
|
+
warnings << 'author is required' if authors.empty?
|
|
26
|
+
warnings << 'text or image is required' unless article_body.present? || images.any?(&:present?)
|
|
27
|
+
warnings
|
|
28
|
+
end
|
|
12
29
|
end
|
|
13
30
|
end
|
|
14
31
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<% title = page&.page_title %>
|
|
4
4
|
<% description = page&.description %>
|
|
5
|
+
<% social_description = page&.resolved_social_description %>
|
|
5
6
|
<% image = page&.resolved_image || default_image_url || nil %>
|
|
6
7
|
<% canonical_url = page&.canonical_url %>
|
|
7
8
|
<% robots_content = page&.robots_content %>
|
|
@@ -23,8 +24,8 @@
|
|
|
23
24
|
<% end %>
|
|
24
25
|
|
|
25
26
|
<meta property="og:title" content="<%= title %>">
|
|
26
|
-
<% if
|
|
27
|
-
<meta property="og:description" content="<%=
|
|
27
|
+
<% if social_description.present? %>
|
|
28
|
+
<meta property="og:description" content="<%= social_description %>">
|
|
28
29
|
<% end %>
|
|
29
30
|
<% if image.present? %>
|
|
30
31
|
<meta property="og:image" content="<%= image %>">
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
|
|
33
34
|
<meta name="twitter:card" content="summary_large_image">
|
|
34
35
|
<meta name="twitter:title" content="<%= title %>">
|
|
35
|
-
<% if
|
|
36
|
-
<meta name="twitter:description" content="<%=
|
|
36
|
+
<% if social_description.present? %>
|
|
37
|
+
<meta name="twitter:description" content="<%= social_description %>">
|
|
37
38
|
<% end %>
|
|
38
39
|
<% if image.present? %>
|
|
39
40
|
<meta name="twitter:image" content="<%= image %>">
|
data/lib/page_structured_data.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "page_structured_data/version"
|
|
2
2
|
require "page_structured_data/engine"
|
|
3
3
|
require_relative "../app/src/page_structured_data/anchors"
|
|
4
|
+
require_relative "../app/src/page_structured_data/json_ld"
|
|
4
5
|
require_relative "../app/src/page_structured_data/breadcrumbs"
|
|
5
6
|
require_relative "../app/src/page_structured_data/page_types/schema_node"
|
|
6
7
|
require_relative "../app/src/page_structured_data/page_types/person"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: page_structured_data
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jey Geethan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -47,6 +47,7 @@ files:
|
|
|
47
47
|
- Rakefile
|
|
48
48
|
- app/src/page_structured_data/anchors.rb
|
|
49
49
|
- app/src/page_structured_data/breadcrumbs.rb
|
|
50
|
+
- app/src/page_structured_data/json_ld.rb
|
|
50
51
|
- app/src/page_structured_data/page.rb
|
|
51
52
|
- app/src/page_structured_data/page_types/article.rb
|
|
52
53
|
- app/src/page_structured_data/page_types/blog_posting.rb
|