page_structured_data 1.0.3 → 1.0.5
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 +19 -0
- data/README.md +14 -4
- data/app/src/page_structured_data/breadcrumbs.rb +17 -12
- data/app/src/page_structured_data/page.rb +8 -3
- data/app/src/page_structured_data/page_types/article.rb +50 -0
- data/app/src/page_structured_data/page_types/blog_posting.rb +4 -36
- data/app/src/page_structured_data/page_types/news_article.rb +4 -36
- data/app/views/page_structured_data/_meta_tags.html.erb +22 -0
- data/lib/page_structured_data/version.rb +1 -1
- data/lib/page_structured_data.rb +7 -0
- metadata +10 -17
- data/app/views/page_structured_data/_meta_tags.html.slim +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5edbf7a0036c6b78d602900b3910cdeb8ee5a16920b47ac9ddaf2411a90f68c9
|
|
4
|
+
data.tar.gz: ae1930791919d1fecd18c525a5b01eb3bb782caf197604461bb17d882d508fd2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f116fb5b5a52658001b8a39fd5820885a8aa71bd0403d901c0f316b0f0541470d9274d55e67f1ee16d53400dcb31aba40364b60e6a1fed3574d7871679c78b0
|
|
7
|
+
data.tar.gz: 23d1d8e077b2b3780b7826a892b7a1636814941e0fbd339f8421a905d5b1d3d4bb6191da0cc529db428c8534da1f3818818da1048d11b7d876a26d32b8b85f45
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 1.0.5 - 2026-05-06
|
|
8
|
+
|
|
9
|
+
- Add tests for HTML escaping in rendered meta tags.
|
|
10
|
+
- Add tests for script-breaking content in JSON-LD output.
|
|
11
|
+
- Add broader JSON-LD escaping coverage for breadcrumbs and article data.
|
|
12
|
+
- Extract shared article JSON-LD behavior for `BlogPosting` and `NewsArticle`.
|
|
13
|
+
- Add `to_h` schema hash APIs for breadcrumbs and article page types.
|
|
14
|
+
- Add tests for pages that render both breadcrumb and page type JSON-LD.
|
|
15
|
+
- Align the gemspec Ruby requirement with the Rails 7 baseline.
|
|
16
|
+
- Add GitHub Actions CI for tests, require verification, and gem build verification.
|
|
17
|
+
- Constrain the Rails dependency to Rails 7.x, matching the tested support baseline.
|
|
18
|
+
- Add `render_default_breadcrumb_json_ld` config to opt out of current-page-only breadcrumb JSON-LD.
|
|
19
|
+
|
|
20
|
+
## 1.0.4 - 2026-05-06
|
|
21
|
+
|
|
22
|
+
- Replace the bundled Slim meta tags partial with ERB so applications are not required to use Slim.
|
|
23
|
+
- Remove the Slim runtime dependency.
|
|
24
|
+
- Add view-rendering tests for the meta tags partial.
|
|
25
|
+
|
|
7
26
|
## 1.0.3 - 2026-05-06
|
|
8
27
|
|
|
9
28
|
- Improve RubyGems metadata, documentation links, and public README presentation.
|
data/README.md
CHANGED
|
@@ -17,9 +17,10 @@ It helps Rails applications render:
|
|
|
17
17
|
|
|
18
18
|
## Requirements
|
|
19
19
|
|
|
20
|
-
- Rails 7.
|
|
21
|
-
-
|
|
22
|
-
|
|
20
|
+
- Rails 7.x
|
|
21
|
+
- Ruby 2.7 or newer
|
|
22
|
+
|
|
23
|
+
Rails 7.0 requires Ruby 2.7 or newer, so this gem follows that same baseline. Rails 8 is not declared as supported yet; add CI coverage before widening the Rails dependency.
|
|
23
24
|
|
|
24
25
|
## Installation
|
|
25
26
|
|
|
@@ -44,12 +45,15 @@ Configure application-wide defaults in an initializer:
|
|
|
44
45
|
Rails.application.config.after_initialize do
|
|
45
46
|
PageStructuredData.config do |config|
|
|
46
47
|
config.base_app_name = "AwesomestApp"
|
|
48
|
+
config.render_default_breadcrumb_json_ld = true
|
|
47
49
|
end
|
|
48
50
|
end
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
`base_app_name` is appended to generated page titles.
|
|
52
54
|
|
|
55
|
+
`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.
|
|
56
|
+
|
|
53
57
|
For example:
|
|
54
58
|
|
|
55
59
|
```ruby
|
|
@@ -126,7 +130,7 @@ Pass the breadcrumbs into the page object:
|
|
|
126
130
|
|
|
127
131
|
This renders `BreadcrumbList` JSON-LD similar to Google's breadcrumb structured data format.
|
|
128
132
|
|
|
129
|
-
Current compatibility note: when no breadcrumb object is passed, `PageStructuredData::Page`
|
|
133
|
+
Current compatibility note: when no breadcrumb object is passed, `PageStructuredData::Page` renders current-page-only breadcrumb JSON-LD by default. To opt out, set `config.render_default_breadcrumb_json_ld = false`.
|
|
130
134
|
|
|
131
135
|
## Article Page Types
|
|
132
136
|
|
|
@@ -197,6 +201,7 @@ PageStructuredData::Breadcrumbs.new(
|
|
|
197
201
|
Important methods:
|
|
198
202
|
|
|
199
203
|
- `titles`: returns breadcrumb titles.
|
|
204
|
+
- `to_h(current_page_title:)`: returns a structured hash for `BreadcrumbList` JSON-LD.
|
|
200
205
|
- `json_ld(current_page_title:)`: returns a `BreadcrumbList` JSON-LD script tag.
|
|
201
206
|
|
|
202
207
|
### Article Page Types
|
|
@@ -223,6 +228,11 @@ PageStructuredData::PageTypes::NewsArticle.new(
|
|
|
223
228
|
|
|
224
229
|
`authors` should be an array of hashes with `:name` and `:url` keys.
|
|
225
230
|
|
|
231
|
+
Important methods:
|
|
232
|
+
|
|
233
|
+
- `to_h`: returns a structured hash for article JSON-LD.
|
|
234
|
+
- `json_ld`: returns an article JSON-LD script tag.
|
|
235
|
+
|
|
226
236
|
## Development
|
|
227
237
|
|
|
228
238
|
Run the test suite:
|
|
@@ -13,15 +13,28 @@ module PageStructuredData
|
|
|
13
13
|
hierarchy.pluck(:title)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
17
|
-
|
|
16
|
+
def to_h(current_page_title:) # rubocop:disable Metrics/MethodLength
|
|
17
|
+
{
|
|
18
18
|
'@context': 'https://schema.org',
|
|
19
19
|
'@type': 'BreadcrumbList',
|
|
20
|
-
'itemListElement':
|
|
21
|
-
}
|
|
20
|
+
'itemListElement': item_list_elements(current_page_title: current_page_title),
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def json_ld(current_page_title:)
|
|
25
|
+
%(
|
|
26
|
+
<script type="application/ld+json">
|
|
27
|
+
#{to_h(current_page_title: current_page_title).to_json}
|
|
28
|
+
</script>
|
|
29
|
+
)
|
|
30
|
+
end
|
|
22
31
|
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def item_list_elements(current_page_title:)
|
|
23
35
|
items = []
|
|
24
36
|
count = 0
|
|
37
|
+
|
|
25
38
|
@hierarchy.each do |page|
|
|
26
39
|
items << {
|
|
27
40
|
'@type': 'ListItem',
|
|
@@ -36,14 +49,6 @@ module PageStructuredData
|
|
|
36
49
|
position: (count += 1),
|
|
37
50
|
name: current_page_title,
|
|
38
51
|
}
|
|
39
|
-
|
|
40
|
-
node['itemListElement'] = items
|
|
41
|
-
|
|
42
|
-
%(
|
|
43
|
-
<script type="application/ld+json">
|
|
44
|
-
#{node.to_json}
|
|
45
|
-
</script>
|
|
46
|
-
)
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
end
|
|
@@ -13,8 +13,6 @@ module PageStructuredData
|
|
|
13
13
|
@extra_title = extra_title
|
|
14
14
|
@breadcrumb = breadcrumb
|
|
15
15
|
@page_type = page_type
|
|
16
|
-
|
|
17
|
-
@breadcrumb = Breadcrumbs.new if breadcrumb.blank?
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
def title_with_hierarchies
|
|
@@ -33,13 +31,20 @@ module PageStructuredData
|
|
|
33
31
|
|
|
34
32
|
def json_lds
|
|
35
33
|
output = []
|
|
36
|
-
output <<
|
|
34
|
+
output << breadcrumb_json_ld if (breadcrumb_json_ld = self.breadcrumb_json_ld).present?
|
|
37
35
|
output << page_type.json_ld if page_type.present?
|
|
38
36
|
output.join
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
private
|
|
42
40
|
|
|
41
|
+
def breadcrumb_json_ld
|
|
42
|
+
return breadcrumb.json_ld(current_page_title: title) if breadcrumb.present?
|
|
43
|
+
return unless PageStructuredData.render_default_breadcrumb_json_ld
|
|
44
|
+
|
|
45
|
+
Breadcrumbs.new.json_ld(current_page_title: title)
|
|
46
|
+
end
|
|
47
|
+
|
|
43
48
|
def base_app_name
|
|
44
49
|
PageStructuredData.base_app_name
|
|
45
50
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageStructuredData
|
|
4
|
+
module PageTypes
|
|
5
|
+
# Shared structured data for schema.org article-like page types.
|
|
6
|
+
class Article
|
|
7
|
+
attr_reader :headline, :images, :published_at, :updated_at, :authors
|
|
8
|
+
|
|
9
|
+
def initialize(headline:, published_at:, updated_at:, images: [], authors: [])
|
|
10
|
+
@headline = headline
|
|
11
|
+
@images = images
|
|
12
|
+
@published_at = published_at
|
|
13
|
+
@updated_at = updated_at
|
|
14
|
+
@authors = authors
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_h
|
|
18
|
+
{
|
|
19
|
+
'@context': 'https://schema.org',
|
|
20
|
+
'@type': schema_type,
|
|
21
|
+
headline: headline,
|
|
22
|
+
image: images,
|
|
23
|
+
datePublished: published_at,
|
|
24
|
+
dateModified: updated_at,
|
|
25
|
+
author: authors.map do |author|
|
|
26
|
+
{
|
|
27
|
+
'@type': 'Person',
|
|
28
|
+
name: author[:name],
|
|
29
|
+
url: author[:url],
|
|
30
|
+
}
|
|
31
|
+
end,
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def json_ld
|
|
36
|
+
%(
|
|
37
|
+
<script type="application/ld+json">
|
|
38
|
+
#{to_h.to_json}
|
|
39
|
+
</script>
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def schema_type
|
|
46
|
+
raise NotImplementedError, "#{self.class.name} must define #schema_type"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -3,43 +3,11 @@
|
|
|
3
3
|
module PageStructuredData
|
|
4
4
|
module PageTypes
|
|
5
5
|
# Basic page metadata for any page
|
|
6
|
-
class BlogPosting
|
|
7
|
-
|
|
6
|
+
class BlogPosting < Article
|
|
7
|
+
private
|
|
8
8
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
@images = images
|
|
12
|
-
@published_at = published_at
|
|
13
|
-
@updated_at = updated_at
|
|
14
|
-
@authors = authors
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def json_ld # rubocop:disable Metrics/MethodLength
|
|
18
|
-
node = {
|
|
19
|
-
'@context': 'https://schema.org',
|
|
20
|
-
'@type': 'BlogPosting',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
node[:headline] = headline
|
|
24
|
-
node[:image] = images
|
|
25
|
-
node[:datePublished] = published_at
|
|
26
|
-
node[:dateModified] = updated_at
|
|
27
|
-
|
|
28
|
-
author_hash = authors.map do |author|
|
|
29
|
-
{
|
|
30
|
-
'@type': 'Person',
|
|
31
|
-
name: author[:name],
|
|
32
|
-
url: author[:url],
|
|
33
|
-
}
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
node[:author] = author_hash
|
|
37
|
-
|
|
38
|
-
%(
|
|
39
|
-
<script type="application/ld+json">
|
|
40
|
-
#{node.to_json}
|
|
41
|
-
</script>
|
|
42
|
-
)
|
|
9
|
+
def schema_type
|
|
10
|
+
'BlogPosting'
|
|
43
11
|
end
|
|
44
12
|
end
|
|
45
13
|
end
|
|
@@ -3,43 +3,11 @@
|
|
|
3
3
|
module PageStructuredData
|
|
4
4
|
module PageTypes
|
|
5
5
|
# Basic page metadata for any page
|
|
6
|
-
class NewsArticle
|
|
7
|
-
|
|
6
|
+
class NewsArticle < Article
|
|
7
|
+
private
|
|
8
8
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
@images = images
|
|
12
|
-
@published_at = published_at
|
|
13
|
-
@updated_at = updated_at
|
|
14
|
-
@authors = authors
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def json_ld # rubocop:disable Metrics/MethodLength
|
|
18
|
-
node = {
|
|
19
|
-
'@context': 'https://schema.org',
|
|
20
|
-
'@type': 'NewsArticle',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
node[:headline] = headline
|
|
24
|
-
node[:image] = images
|
|
25
|
-
node[:datePublished] = published_at
|
|
26
|
-
node[:dateModified] = updated_at
|
|
27
|
-
|
|
28
|
-
author_hash = authors.map do |author|
|
|
29
|
-
{
|
|
30
|
-
'@type': 'Person',
|
|
31
|
-
name: author[:name],
|
|
32
|
-
url: author[:url],
|
|
33
|
-
}
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
node[:author] = author_hash
|
|
37
|
-
|
|
38
|
-
%(
|
|
39
|
-
<script type="application/ld+json">
|
|
40
|
-
#{node.to_json}
|
|
41
|
-
</script>
|
|
42
|
-
)
|
|
9
|
+
def schema_type
|
|
10
|
+
'NewsArticle'
|
|
43
11
|
end
|
|
44
12
|
end
|
|
45
13
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% default_image_url = local_assigns[:default_image_url] %>
|
|
2
|
+
|
|
3
|
+
<% title = page&.page_title %>
|
|
4
|
+
<% description = page&.description %>
|
|
5
|
+
<% image = page&.image || default_image_url || nil %>
|
|
6
|
+
|
|
7
|
+
<title><%= title %></title>
|
|
8
|
+
|
|
9
|
+
<meta name="title" content="<%= title %>">
|
|
10
|
+
<meta name="description" content="<%= description %>">
|
|
11
|
+
<meta name="image" content="<%= image %>">
|
|
12
|
+
|
|
13
|
+
<meta property="og:title" content="<%= title %>">
|
|
14
|
+
<meta property="og:description" content="<%= description %>">
|
|
15
|
+
<meta property="og:image" content="<%= image %>">
|
|
16
|
+
|
|
17
|
+
<meta property="twitter:card" content="summary_large_image">
|
|
18
|
+
<meta property="twitter:title" content="<%= title %>">
|
|
19
|
+
<meta property="twitter:description" content="<%= description %>">
|
|
20
|
+
<meta property="twitter:image" content="<%= image %>">
|
|
21
|
+
|
|
22
|
+
<%= page&.json_lds&.html_safe %>
|
data/lib/page_structured_data.rb
CHANGED
|
@@ -4,9 +4,16 @@ require "page_structured_data/engine"
|
|
|
4
4
|
module PageStructuredData
|
|
5
5
|
class << self
|
|
6
6
|
attr_accessor :base_app_name
|
|
7
|
+
attr_writer :render_default_breadcrumb_json_ld
|
|
7
8
|
|
|
8
9
|
def config
|
|
9
10
|
yield self
|
|
10
11
|
end
|
|
12
|
+
|
|
13
|
+
def render_default_breadcrumb_json_ld
|
|
14
|
+
return true if @render_default_breadcrumb_json_ld.nil?
|
|
15
|
+
|
|
16
|
+
@render_default_breadcrumb_json_ld
|
|
17
|
+
end
|
|
11
18
|
end
|
|
12
19
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jey Geethan
|
|
@@ -14,30 +14,22 @@ dependencies:
|
|
|
14
14
|
name: rails
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 7.0
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
19
|
+
version: '7.0'
|
|
24
20
|
- - ">="
|
|
25
21
|
- !ruby/object:Gem::Version
|
|
26
22
|
version: 7.0.0
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: slim
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 4.1.0
|
|
34
23
|
type: :runtime
|
|
35
24
|
prerelease: false
|
|
36
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
26
|
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '7.0'
|
|
38
30
|
- - ">="
|
|
39
31
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
32
|
+
version: 7.0.0
|
|
41
33
|
description: PageStructuredData gives Rails applications a small page object and view
|
|
42
34
|
partial for rendering page titles, basic meta tags, Open Graph tags, Twitter card
|
|
43
35
|
tags, breadcrumb JSON-LD, and article JSON-LD.
|
|
@@ -61,10 +53,11 @@ files:
|
|
|
61
53
|
- app/src/page_structured_data/anchors.rb
|
|
62
54
|
- app/src/page_structured_data/breadcrumbs.rb
|
|
63
55
|
- app/src/page_structured_data/page.rb
|
|
56
|
+
- app/src/page_structured_data/page_types/article.rb
|
|
64
57
|
- app/src/page_structured_data/page_types/blog_posting.rb
|
|
65
58
|
- app/src/page_structured_data/page_types/news_article.rb
|
|
66
59
|
- app/views/layouts/page_structured_data/application.html.erb
|
|
67
|
-
- app/views/page_structured_data/_meta_tags.html.
|
|
60
|
+
- app/views/page_structured_data/_meta_tags.html.erb
|
|
68
61
|
- config/routes.rb
|
|
69
62
|
- lib/page_structured_data.rb
|
|
70
63
|
- lib/page_structured_data/engine.rb
|
|
@@ -89,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
82
|
requirements:
|
|
90
83
|
- - ">="
|
|
91
84
|
- !ruby/object:Gem::Version
|
|
92
|
-
version: 2.
|
|
85
|
+
version: 2.7.0
|
|
93
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
87
|
requirements:
|
|
95
88
|
- - ">="
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
- default_image_url = local_assigns[:default_image_url]
|
|
2
|
-
|
|
3
|
-
- title = page&.page_title
|
|
4
|
-
- description = page&.description
|
|
5
|
-
- image = page&.image || default_image_url || nil
|
|
6
|
-
|
|
7
|
-
title = title
|
|
8
|
-
|
|
9
|
-
meta{name="title" content=title}
|
|
10
|
-
meta{name="description" content=description}
|
|
11
|
-
meta{name="image" content=image}
|
|
12
|
-
|
|
13
|
-
meta{property="og:title" content=title}
|
|
14
|
-
meta{property="og:description" content=description}
|
|
15
|
-
meta{property="og:image" content=image}
|
|
16
|
-
|
|
17
|
-
meta{property="twitter:card" content="summary_large_image"}
|
|
18
|
-
meta{property="twitter:title" content=title}
|
|
19
|
-
meta{property="twitter:description" content=description}
|
|
20
|
-
meta{property="twitter:image" content=image}
|
|
21
|
-
|
|
22
|
-
= page&.json_lds&.html_safe
|