page_structured_data 1.0.13 → 1.0.14

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: 8e263a2b3ad541c2b5e72aa79e71d47d72803ddb8a4ac286898e6c6f35105394
4
- data.tar.gz: 96f01f8fc820456eb69ba5a351f8971022758088d0a5633240c38c7831df43f9
3
+ metadata.gz: 1e36ab494ebd4db8743deb6d8415e826c2186f7c28a7e2927c8a08df4decabd6
4
+ data.tar.gz: b48c8723a6ad4a3ee5aa44be5e6b36d6764abbd433a3daedb1e3278f32cf7b2b
5
5
  SHA512:
6
- metadata.gz: 802b70166a18b5475e702822cfb7b0c565c7fc59d8dd5059831261676831e1150c95b8f0e1e11b0de493aa120f3e23f2ddd9a619238ebcdd5f25c8f3920d3d6e
7
- data.tar.gz: bc818be71ea5d603ac3fb6b84e99dcf79fd76607f3272039cde49d65c772f365d812c0f7d260cdf7beda88762012994ae207b309e7a5dc21b94ab129608e0c9e
6
+ metadata.gz: 1c9dd473ced4959c0acc17940b212d0c71b760efe3a0cfe119d74845ab10b76e081363b26d4138f6b5d66a5e62d0f7c36623c5603ac78ec8565350e2cd53dbcf
7
+ data.tar.gz: eefd736852c02d3ec1f02a58e7911f931d8882ce09854364c9b028cd1a11aa1872c3fe1a79beb4c379b8468e1b3db7871a00331dc47522573da9abc64bfa6dce
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to this project are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 1.0.14 - 2026-07-05
8
+
9
+ - Add page-level `robots` meta tag support.
10
+ - Add richer optional schema fields for article-like page types.
11
+ - Add README common pattern examples.
12
+ - Add focused rendered metadata output coverage.
13
+ - Add soft validation helpers with `warnings` and `valid?`.
14
+ - Improve README and gem metadata positioning.
15
+
7
16
  ## 1.0.13 - 2026-05-06
8
17
 
9
18
  - Add `PageStructuredData::PageTypes::Person` for reusable schema.org person values.
data/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/page_structured_data.svg)](https://rubygems.org/gems/page_structured_data)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](MIT-LICENSE)
5
5
 
6
- PageStructuredData is a small Rails engine for rendering page-level SEO and social sharing metadata from one page object.
6
+ PageStructuredData is a small Rails engine that keeps SEO metadata, social sharing tags, and JSON-LD structured data in one page object and one view partial.
7
+
8
+ Use it when a Rails app has metadata spread across layouts, helpers, presenters, and page-specific templates. PageStructuredData gives each page one explicit metadata object, then renders the `<title>`, meta tags, Open Graph tags, Twitter Card tags, breadcrumbs, and schema.org JSON-LD consistently.
7
9
 
8
10
  It helps Rails applications render:
9
11
 
@@ -98,7 +100,8 @@ Set `@page_meta` in the controller or view before the layout renders:
98
100
  description: "Welcome to my page",
99
101
  image: image_url("social/home.png"),
100
102
  canonical_url: home_url,
101
- fallback_image: image_url("social/default.png")
103
+ fallback_image: image_url("social/default.png"),
104
+ robots: "index,follow"
102
105
  )
103
106
  ```
104
107
 
@@ -265,6 +268,182 @@ website_page_type = PageStructuredData::PageTypes::WebSite.new(
265
268
  )
266
269
  ```
267
270
 
271
+ ## Common Patterns
272
+
273
+ ### Article Page
274
+
275
+ Use `BlogPosting` or `NewsArticle` when a page represents editorial content:
276
+
277
+ ```ruby
278
+ publisher = PageStructuredData::PageTypes::Organization.new(
279
+ name: "Example",
280
+ url: root_url
281
+ )
282
+
283
+ author = PageStructuredData::PageTypes::Person.new(
284
+ name: @article.author.name,
285
+ url: author_url(@article.author)
286
+ )
287
+
288
+ article_page_type = PageStructuredData::PageTypes::BlogPosting.new(
289
+ headline: @article.title,
290
+ description: @article.summary,
291
+ article_body: @article.body.to_plain_text,
292
+ url: article_url(@article),
293
+ main_entity_of_page: article_url(@article),
294
+ publisher: publisher,
295
+ article_section: @article.category.name,
296
+ keywords: @article.tags.pluck(:name),
297
+ word_count: @article.word_count,
298
+ in_language: "en",
299
+ published_at: @article.published_at,
300
+ updated_at: @article.updated_at,
301
+ authors: [author],
302
+ image: url_for(@article.cover_image),
303
+ likes_count: @article.likes_count
304
+ )
305
+
306
+ @page_meta = PageStructuredData::Page.new(
307
+ title: @article.title,
308
+ description: @article.summary,
309
+ image: url_for(@article.cover_image),
310
+ canonical_url: article_url(@article),
311
+ breadcrumb: article_breadcrumbs,
312
+ page_type: article_page_type
313
+ )
314
+ ```
315
+
316
+ ### Homepage
317
+
318
+ Use both `Organization` and `WebSite` when the page represents the public home of a site or organization:
319
+
320
+ ```ruby
321
+ founder = PageStructuredData::PageTypes::Person.new(
322
+ name: "Jane Doe",
323
+ url: "https://example.com/jane"
324
+ )
325
+
326
+ organization = PageStructuredData::PageTypes::Organization.new(
327
+ name: "Example",
328
+ url: root_url,
329
+ description: "Useful software from Example",
330
+ logo: image_url("logo.png"),
331
+ same_as: ["https://github.com/example"],
332
+ founder: founder
333
+ )
334
+
335
+ website = PageStructuredData::PageTypes::WebSite.new(
336
+ name: "Example",
337
+ url: root_url,
338
+ description: "Useful software from Example",
339
+ publisher: organization
340
+ )
341
+
342
+ @page_meta = PageStructuredData::Page.new(
343
+ title: "Example",
344
+ description: "Useful software from Example",
345
+ image: image_url("social/home.png"),
346
+ canonical_url: root_url,
347
+ page_types: [organization, website],
348
+ render_breadcrumb_json_ld: false
349
+ )
350
+ ```
351
+
352
+ ### Forum Or Community Post
353
+
354
+ Use `DiscussionForumPosting` for public, user-authored, timestamped posts:
355
+
356
+ ```ruby
357
+ post_author = PageStructuredData::PageTypes::Person.new(
358
+ name: @post.user.name,
359
+ url: user_url(@post.user)
360
+ )
361
+
362
+ post_page_type = PageStructuredData::PageTypes::DiscussionForumPosting.new(
363
+ headline: @post.title,
364
+ text: @post.content_plaintext,
365
+ url: post_url(@post),
366
+ published_at: @post.created_at,
367
+ updated_at: @post.updated_at,
368
+ authors: [post_author],
369
+ comments_count: @post.comments_count
370
+ )
371
+
372
+ @page_meta = PageStructuredData::Page.new(
373
+ title: @post.title,
374
+ description: @post.excerpt,
375
+ canonical_url: post_url(@post),
376
+ page_type: post_page_type
377
+ )
378
+ ```
379
+
380
+ Only pass engagement counts that are public and visible on the rendered page.
381
+
382
+ ### Page-Local Site Name
383
+
384
+ Override or suppress the global app name for one page:
385
+
386
+ ```ruby
387
+ PageStructuredData.base_app_name = "Example"
388
+
389
+ PageStructuredData::Page.new(
390
+ title: "Docs",
391
+ base_app_name: "Developer Docs"
392
+ ).page_title
393
+ # => "Docs - Developer Docs"
394
+
395
+ PageStructuredData::Page.new(
396
+ title: "Minimal",
397
+ base_app_name: ""
398
+ ).page_title
399
+ # => "Minimal"
400
+ ```
401
+
402
+ ### Breadcrumb JSON-LD Control
403
+
404
+ Control generated breadcrumb JSON-LD per page:
405
+
406
+ ```ruby
407
+ PageStructuredData::Page.new(
408
+ title: "Landing Page",
409
+ render_breadcrumb_json_ld: false
410
+ )
411
+ ```
412
+
413
+ When the global default is disabled, a page can still opt in:
414
+
415
+ ```ruby
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
+
424
+ ### Paginated Archive
425
+
426
+ Use `robots` for archive, search, filtered, or paginated pages that should be crawlable but not indexed:
427
+
428
+ ```ruby
429
+ @page_meta = PageStructuredData::Page.new(
430
+ title: "Articles",
431
+ extra_title: "Page #{@pagy.page}",
432
+ description: "Browse articles from Example",
433
+ canonical_url: articles_url(page: @pagy.page),
434
+ robots: "noindex,follow"
435
+ )
436
+ ```
437
+
438
+ `robots` can also be passed as an array:
439
+
440
+ ```ruby
441
+ PageStructuredData::Page.new(
442
+ title: "Articles",
443
+ robots: ["noindex", "follow"]
444
+ )
445
+ ```
446
+
268
447
  ## API Reference
269
448
 
270
449
  ### `PageStructuredData::Page`
@@ -281,18 +460,42 @@ PageStructuredData::Page.new(
281
460
  canonical_url: nil,
282
461
  fallback_image: nil,
283
462
  base_app_name: nil,
284
- render_breadcrumb_json_ld: nil
463
+ render_breadcrumb_json_ld: nil,
464
+ robots: nil
285
465
  )
286
466
  ```
287
467
 
288
468
  `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.
289
469
  `render_breadcrumb_json_ld` can be set to `true` or `false` for one page. Leave it as `nil` to use the global `PageStructuredData.render_default_breadcrumb_json_ld` behavior for generated default breadcrumbs. Explicit breadcrumb objects still render when the global default is disabled unless the page sets `render_breadcrumb_json_ld: false`.
470
+ `robots` can be a string or array and renders a `<meta name="robots">` tag when present.
290
471
 
291
472
  Important methods:
292
473
 
293
474
  - `page_title`: returns the composed page title.
294
475
  - `json_lds`: returns the JSON-LD script tags for breadcrumbs and page type data.
295
476
  - `resolved_image`: returns `image` or `fallback_image`.
477
+ - `robots_content`: returns the rendered robots directives.
478
+ - `warnings`: returns soft validation warnings for the page and page types.
479
+ - `valid?`: returns `true` when `warnings` is empty.
480
+
481
+ ### Soft Validation
482
+
483
+ Page objects and page type objects expose non-blocking validation helpers:
484
+
485
+ ```ruby
486
+ page_type = PageStructuredData::PageTypes::Organization.new(
487
+ name: nil,
488
+ url: nil
489
+ )
490
+
491
+ page_type.warnings
492
+ # => ["name is required", "url is required"]
493
+
494
+ page_type.valid?
495
+ # => false
496
+ ```
497
+
498
+ `warnings` does not stop rendering. It is intended for tests, previews, and development checks where you want to catch incomplete structured data before publishing a page.
296
499
 
297
500
  ### `PageStructuredData::Breadcrumbs`
298
501
 
@@ -320,9 +523,16 @@ PageStructuredData::PageTypes::BlogPosting.new(
320
523
  images: [],
321
524
  authors: [],
322
525
  image: nil,
526
+ description: nil,
323
527
  article_body: nil,
324
528
  text: nil,
325
529
  url: nil,
530
+ main_entity_of_page: nil,
531
+ publisher: nil,
532
+ article_section: nil,
533
+ keywords: nil,
534
+ word_count: nil,
535
+ in_language: nil,
326
536
  interaction_statistics: [],
327
537
  likes_count: nil,
328
538
  comments_count: nil,
@@ -338,9 +548,16 @@ PageStructuredData::PageTypes::NewsArticle.new(
338
548
  images: [],
339
549
  authors: [],
340
550
  image: nil,
551
+ description: nil,
341
552
  article_body: nil,
342
553
  text: nil,
343
554
  url: nil,
555
+ main_entity_of_page: nil,
556
+ publisher: nil,
557
+ article_section: nil,
558
+ keywords: nil,
559
+ word_count: nil,
560
+ in_language: nil,
344
561
  interaction_statistics: [],
345
562
  likes_count: nil,
346
563
  comments_count: nil,
@@ -351,6 +568,9 @@ PageStructuredData::PageTypes::NewsArticle.new(
351
568
  `authors` can be an array of hashes, `PageStructuredData::PageTypes::Person` objects, or other objects that respond to `to_h`.
352
569
  `image` is a convenience option for one image URL. Use `images` when passing multiple image URLs.
353
570
  `text` is an alias for `article_body`.
571
+ `main_entity_of_page` can be a URL, hash, or object that responds to `to_h`.
572
+ `publisher` can be a hash or another page type that responds to `to_h`, such as `PageStructuredData::PageTypes::Organization`.
573
+ `keywords` can be a string or an array.
354
574
  `interaction_statistics` should be an array of `PageStructuredData::PageTypes::InteractionStatistic` objects or schema-compatible hashes.
355
575
 
356
576
  Important methods:
@@ -366,9 +586,16 @@ PageStructuredData::PageTypes::DiscussionForumPosting.new(
366
586
  images: [],
367
587
  authors: [],
368
588
  image: nil,
589
+ description: nil,
369
590
  article_body: nil,
370
591
  text: nil,
371
592
  url: nil,
593
+ main_entity_of_page: nil,
594
+ publisher: nil,
595
+ article_section: nil,
596
+ keywords: nil,
597
+ word_count: nil,
598
+ in_language: nil,
372
599
  interaction_statistics: [],
373
600
  likes_count: nil,
374
601
  comments_count: nil,
@@ -4,11 +4,11 @@ module PageStructuredData
4
4
  # Basic page metadata for any page
5
5
  class Page
6
6
  attr_reader :title, :description, :image, :extra_title, :breadcrumb, :page_type, :page_types, :canonical_url,
7
- :fallback_image, :base_app_name, :render_breadcrumb_json_ld
7
+ :fallback_image, :base_app_name, :render_breadcrumb_json_ld, :robots
8
8
 
9
9
  def initialize(title:, description: nil, image: nil, # rubocop:disable Metrics/ParameterLists
10
10
  extra_title: '', breadcrumb: nil, page_type: nil, page_types: nil, canonical_url: nil,
11
- fallback_image: nil, base_app_name: nil, render_breadcrumb_json_ld: nil)
11
+ fallback_image: nil, base_app_name: nil, render_breadcrumb_json_ld: nil, robots: nil)
12
12
  @title = title
13
13
  @description = description
14
14
  @image = image
@@ -20,6 +20,7 @@ module PageStructuredData
20
20
  @fallback_image = fallback_image
21
21
  @base_app_name = base_app_name
22
22
  @render_breadcrumb_json_ld = render_breadcrumb_json_ld
23
+ @robots = robots
23
24
  end
24
25
 
25
26
  def title_with_hierarchies
@@ -47,12 +48,36 @@ module PageStructuredData
47
48
  image || fallback_image
48
49
  end
49
50
 
51
+ def robots_content
52
+ Array(robots).compact.join(',')
53
+ end
54
+
55
+ def warnings
56
+ page_warnings + page_type_warnings
57
+ end
58
+
59
+ def valid?
60
+ warnings.empty?
61
+ end
62
+
50
63
  private
51
64
 
52
65
  def resolved_page_types
53
66
  Array.wrap(page_types.presence || page_type).compact
54
67
  end
55
68
 
69
+ def page_warnings
70
+ title.present? ? [] : ['title is required']
71
+ end
72
+
73
+ def page_type_warnings
74
+ resolved_page_types.each_with_index.flat_map do |resolved_page_type, index|
75
+ next [] unless resolved_page_type.respond_to?(:warnings)
76
+
77
+ resolved_page_type.warnings.map { |warning| "page type #{index + 1}: #{warning}" }
78
+ end
79
+ end
80
+
56
81
  def breadcrumb_json_ld
57
82
  return if render_breadcrumb_json_ld == false
58
83
  return breadcrumb.json_ld(current_page_title: title) if breadcrumb.present?
@@ -6,18 +6,28 @@ module PageStructuredData
6
6
  class Article
7
7
  include SchemaNode
8
8
 
9
- attr_reader :headline, :images, :published_at, :updated_at, :authors, :article_body, :url,
9
+ attr_reader :headline, :images, :published_at, :updated_at, :authors, :description, :article_body, :url,
10
+ :main_entity_of_page, :publisher, :article_section, :keywords, :word_count, :in_language,
10
11
  :interaction_statistics, :likes_count, :comments_count, :shares_count
11
12
 
12
13
  def initialize(headline:, published_at:, updated_at:, images: [], authors: [], image: nil, article_body: nil, text: nil,
13
- url: nil, interaction_statistics: [], likes_count: nil, comments_count: nil, shares_count: nil)
14
+ description: nil, url: nil, main_entity_of_page: nil, publisher: nil, article_section: nil,
15
+ keywords: nil, word_count: nil, in_language: nil, interaction_statistics: [], likes_count: nil,
16
+ comments_count: nil, shares_count: nil)
14
17
  @headline = headline
15
18
  @images = image.present? ? Array(image) : Array(images)
16
19
  @published_at = published_at
17
20
  @updated_at = updated_at
18
21
  @authors = Array(authors)
22
+ @description = description
19
23
  @article_body = article_body || text
20
24
  @url = url
25
+ @main_entity_of_page = main_entity_of_page
26
+ @publisher = publisher
27
+ @article_section = article_section
28
+ @keywords = keywords
29
+ @word_count = word_count
30
+ @in_language = in_language
21
31
  @interaction_statistics = Array(interaction_statistics)
22
32
  @likes_count = likes_count
23
33
  @comments_count = comments_count
@@ -35,8 +45,15 @@ module PageStructuredData
35
45
  author: authors.map { |author| author_to_h(author) },
36
46
  }
37
47
 
48
+ node[:description] = description if description.present?
38
49
  node[:articleBody] = article_body if article_body.present?
39
50
  node[:url] = url if url.present?
51
+ node[:mainEntityOfPage] = object_to_h(main_entity_of_page) if main_entity_of_page.present?
52
+ node[:publisher] = object_to_h(publisher) if publisher.present?
53
+ node[:articleSection] = article_section if article_section.present?
54
+ node[:keywords] = keywords if keywords.present?
55
+ node[:wordCount] = word_count if word_count.present?
56
+ node[:inLanguage] = object_to_h(in_language) if in_language.present?
40
57
  node[:interactionStatistic] = interaction_statistics_to_h if interaction_statistics_to_h.any?
41
58
 
42
59
  node
@@ -50,6 +67,14 @@ module PageStructuredData
50
67
  )
51
68
  end
52
69
 
70
+ def warnings
71
+ required_attribute_warnings(
72
+ headline: headline,
73
+ published_at: published_at,
74
+ updated_at: updated_at
75
+ ) + author_warnings + publisher_warnings + interaction_statistic_warnings
76
+ end
77
+
53
78
  private
54
79
 
55
80
  def schema_type
@@ -68,6 +93,25 @@ module PageStructuredData
68
93
  )
69
94
  end
70
95
 
96
+ def author_warnings
97
+ authors.each_with_index.flat_map do |author, index|
98
+ if author.respond_to?(:warnings)
99
+ author.warnings.map { |warning| "author #{index + 1}: #{warning}" }
100
+ else
101
+ author_hash = object_to_h(author) || {}
102
+ required_attribute_warnings(name: author_hash[:name] || author_hash['name']).map do |warning|
103
+ "author #{index + 1}: #{warning}"
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def publisher_warnings
110
+ return [] unless publisher.respond_to?(:warnings)
111
+
112
+ publisher.warnings.map { |warning| "publisher: #{warning}" }
113
+ end
114
+
71
115
  def interaction_statistics_to_h
72
116
  @interaction_statistics_to_h ||= all_interaction_statistics.map do |interaction_statistic|
73
117
  if interaction_statistic.respond_to?(:to_h)
@@ -95,6 +139,14 @@ module PageStructuredData
95
139
 
96
140
  InteractionStatistic.new(interaction_type: interaction_type, user_interaction_count: count)
97
141
  end
142
+
143
+ def interaction_statistic_warnings
144
+ all_interaction_statistics.each_with_index.flat_map do |interaction_statistic, index|
145
+ next [] unless interaction_statistic.respond_to?(:warnings)
146
+
147
+ interaction_statistic.warnings.map { |warning| "interaction statistic #{index + 1}: #{warning}" }
148
+ end
149
+ end
98
150
  end
99
151
  end
100
152
  end
@@ -44,6 +44,13 @@ module PageStructuredData
44
44
  )
45
45
  end
46
46
 
47
+ def warnings
48
+ required_attribute_warnings(
49
+ interaction_type: interaction_type,
50
+ user_interaction_count: user_interaction_count
51
+ )
52
+ end
53
+
47
54
  private
48
55
 
49
56
  def interaction_type_to_h
@@ -40,8 +40,21 @@ module PageStructuredData
40
40
  )
41
41
  end
42
42
 
43
+ def warnings
44
+ required_attribute_warnings(name: name, url: url) + nested_warnings
45
+ end
46
+
43
47
  private
44
48
 
49
+ def nested_warnings
50
+ [founder, parent_organization].each_with_index.flat_map do |node, index|
51
+ next [] unless node.respond_to?(:warnings)
52
+
53
+ prefix = index.zero? ? 'founder' : 'parent organization'
54
+ node.warnings.map { |warning| "#{prefix}: #{warning}" }
55
+ end
56
+ end
57
+
45
58
  def parent_organization_to_h
46
59
  return object_to_h(parent_organization) if parent_organization.respond_to?(:to_h) && !parent_organization.is_a?(Hash)
47
60
  return unless parent_organization.present?
@@ -24,6 +24,10 @@ module PageStructuredData
24
24
  sameAs: same_as
25
25
  )
26
26
  end
27
+
28
+ def warnings
29
+ required_attribute_warnings(name: name)
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -4,6 +4,14 @@ module PageStructuredData
4
4
  module PageTypes
5
5
  # Shared helpers for schema.org hash values.
6
6
  module SchemaNode
7
+ def warnings
8
+ []
9
+ end
10
+
11
+ def valid?
12
+ warnings.empty?
13
+ end
14
+
7
15
  private
8
16
 
9
17
  def compact_node(node)
@@ -23,6 +31,12 @@ module PageStructuredData
23
31
  def blank_schema_value?(value)
24
32
  value.nil? || (value.respond_to?(:empty?) && value.empty?)
25
33
  end
34
+
35
+ def required_attribute_warnings(attributes)
36
+ attributes.each_with_object([]) do |(name, value), warnings|
37
+ warnings << "#{name} is required" if blank_schema_value?(value)
38
+ end
39
+ end
26
40
  end
27
41
  end
28
42
  end
@@ -36,6 +36,17 @@ module PageStructuredData
36
36
  )
37
37
  end
38
38
 
39
+ def warnings
40
+ required_attribute_warnings(name: name, url: url) + nested_warnings
41
+ end
42
+
43
+ private
44
+
45
+ def nested_warnings
46
+ return [] unless publisher.respond_to?(:warnings)
47
+
48
+ publisher.warnings.map { |warning| "publisher: #{warning}" }
49
+ end
39
50
  end
40
51
  end
41
52
  end
@@ -4,11 +4,15 @@
4
4
  <% description = page&.description %>
5
5
  <% image = page&.resolved_image || default_image_url || nil %>
6
6
  <% canonical_url = page&.canonical_url %>
7
+ <% robots_content = page&.robots_content %>
7
8
 
8
9
  <title><%= title %></title>
9
10
  <% if canonical_url.present? %>
10
11
  <link rel="canonical" href="<%= canonical_url %>">
11
12
  <% end %>
13
+ <% if robots_content.present? %>
14
+ <meta name="robots" content="<%= robots_content %>">
15
+ <% end %>
12
16
 
13
17
  <meta name="title" content="<%= title %>">
14
18
  <% if description.present? %>
@@ -1,3 +1,3 @@
1
1
  module PageStructuredData
2
- VERSION = "1.0.13"
2
+ VERSION = "1.0.14"
3
3
  end
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.13
4
+ version: 1.0.14
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-05-06 00:00:00.000000000 Z
11
+ date: 2026-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,10 +30,11 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '9.0'
33
- description: PageStructuredData gives Rails applications a small page object and view
34
- partial for rendering page titles, basic meta tags, Open Graph tags, Twitter card
35
- tags, breadcrumb JSON-LD, article and forum post JSON-LD, Person, Organization,
36
- and WebSite JSON-LD, and public interaction statistics.
33
+ description: PageStructuredData keeps Rails SEO metadata, social sharing tags, and
34
+ schema.org JSON-LD in one page object and one view partial. It renders page titles,
35
+ basic meta tags, Open Graph tags, Twitter Card tags, breadcrumb JSON-LD, article
36
+ and forum post JSON-LD, Person, Organization, and WebSite JSON-LD, and public interaction
37
+ statistics.
37
38
  email:
38
39
  - opensource@rocketapex.com
39
40
  executables: []
@@ -91,5 +92,5 @@ requirements: []
91
92
  rubygems_version: 3.3.7
92
93
  signing_key:
93
94
  specification_version: 4
94
- summary: Render SEO, social, and JSON-LD metadata for Rails pages
95
+ summary: One Rails page object for SEO, social, and JSON-LD metadata
95
96
  test_files: []