better_seo 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -0
- data/README.md +794 -13
- data/lib/better_seo/version.rb +1 -1
- data/lib/better_seo.rb +8 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b1f457db06f6ed1a5654363c639448945eb0c8ee11f311203333edf3de5b499
|
|
4
|
+
data.tar.gz: bbb3f5e0f831a1f3437b1dd171b4e7c8cc953fc654b102670037da19cc96ae73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0978f9a96df030f0b10c10e409c9938c7cca9d5b881212c10135de040f6e51cbd1631f5b6d76ffe9af9efad722336d4f522edb7abf6872d7abd86ae2e516f7ce'
|
|
7
|
+
data.tar.gz: e3fcdde3228d074d75f3a47abcfb81c5145c827191512642745254df0c93c8be1f7a2f82c0d9aaed783c64951dc1b753b436e928fd29d6fc22fce6091b97ea7b
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2025-01-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Structured Data (JSON-LD) support with comprehensive Schema.org implementation
|
|
14
|
+
- `StructuredData::Base` - Generic base class for all structured data types
|
|
15
|
+
- `StructuredData::Organization` - Company/organization information with full schema support
|
|
16
|
+
- `StructuredData::Article` - Blog posts, news articles with author/publisher metadata
|
|
17
|
+
- `StructuredData::Person` - Author profiles, team members with social links
|
|
18
|
+
- `StructuredData::Generator` - Helper class with factory methods
|
|
19
|
+
- Structured data features
|
|
20
|
+
- Fluent API with method chaining for all types
|
|
21
|
+
- Automatic nested object handling (Person within Article, etc.)
|
|
22
|
+
- JSON-LD generation with `to_json` and `to_script_tag` methods
|
|
23
|
+
- Schema.org compliant output format
|
|
24
|
+
- Address handling with PostalAddress schema
|
|
25
|
+
- Social profile links with sameAs property
|
|
26
|
+
- Rails integration patterns
|
|
27
|
+
- View helpers for structured data
|
|
28
|
+
- Layout integration examples
|
|
29
|
+
- Complete production examples
|
|
30
|
+
- Comprehensive documentation
|
|
31
|
+
- 300+ lines of structured data documentation in README
|
|
32
|
+
- Complete examples for Organization, Article, Person
|
|
33
|
+
- Rails integration patterns and helper methods
|
|
34
|
+
- Nested data examples
|
|
35
|
+
|
|
36
|
+
### Test Coverage
|
|
37
|
+
- 491 tests passing (+107 from v0.5.0)
|
|
38
|
+
- 99.64% code coverage (820/823 lines)
|
|
39
|
+
- 117 new tests for structured data functionality
|
|
40
|
+
|
|
41
|
+
## [0.5.0] - 2025-01-23
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- Sitemap generation system with comprehensive XML sitemap support
|
|
45
|
+
- `Sitemap::UrlEntry` - Individual URL entry with full sitemap.org protocol support
|
|
46
|
+
- `Sitemap::Builder` - Fluent API for building sitemaps with method chaining
|
|
47
|
+
- `Sitemap::Generator` - High-level generator with multiple generation strategies
|
|
48
|
+
- Sitemap generation methods
|
|
49
|
+
- `generate` - Generate from block with fluent builder
|
|
50
|
+
- `generate_from` - Generate from array of URLs
|
|
51
|
+
- `generate_from_collection` - Generate from model collections with lambda support
|
|
52
|
+
- `write_to_file` - Write sitemap directly to file with automatic directory creation
|
|
53
|
+
- URL entry features
|
|
54
|
+
- Support for all sitemap attributes: loc, lastmod, changefreq, priority
|
|
55
|
+
- Automatic date formatting (Date, Time, DateTime objects)
|
|
56
|
+
- XML entity escaping for security
|
|
57
|
+
- Validation for URL format, protocol (HTTP/HTTPS), and required fields
|
|
58
|
+
- Dynamic attribute generation
|
|
59
|
+
- Lambda/Proc support for dynamic lastmod, changefreq, and priority
|
|
60
|
+
- Conditional logic based on model attributes
|
|
61
|
+
- Rails integration examples
|
|
62
|
+
- Controller actions for dynamic sitemap serving
|
|
63
|
+
- Rake tasks for sitemap generation
|
|
64
|
+
- Service object patterns for production use
|
|
65
|
+
- Multi-model sitemap examples
|
|
66
|
+
- Comprehensive error handling
|
|
67
|
+
- 28 error class tests added
|
|
68
|
+
- Full coverage of all error types
|
|
69
|
+
|
|
70
|
+
### Test Coverage
|
|
71
|
+
- 384 tests passing (+98 from v0.4.0)
|
|
72
|
+
- 99.71% code coverage (681/683 lines)
|
|
73
|
+
- 69 new tests for sitemap functionality
|
|
74
|
+
|
|
10
75
|
## [0.4.0] - 2025-01-23
|
|
11
76
|
|
|
12
77
|
### Added
|
data/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive SEO gem for Ruby and Rails applications. BetterSeo provides a clean, fluent DSL for managing meta tags, Open Graph, Twitter Cards, structured data, sitemaps, and more.
|
|
4
4
|
|
|
5
|
-
[](https://github.com/yourusername/better_seo)
|
|
6
|
+
[](https://github.com/yourusername/better_seo)
|
|
7
7
|
[](https://www.ruby-lang.org)
|
|
8
8
|
[](https://rubyonrails.org)
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
|
-
### ✅ Implemented (v0.
|
|
12
|
+
### ✅ Implemented (v0.6.0)
|
|
13
13
|
|
|
14
14
|
- **Core Configuration System**
|
|
15
15
|
- Singleton configuration with block-style setup
|
|
@@ -38,23 +38,51 @@ A comprehensive SEO gem for Ruby and Rails applications. BetterSeo provides a cl
|
|
|
38
38
|
- Automatic HTML safety with `raw` helper
|
|
39
39
|
- Integration with global configuration defaults
|
|
40
40
|
|
|
41
|
+
- **Sitemap Generation**
|
|
42
|
+
- **XML Sitemap Builder**: Fluent API for building sitemaps
|
|
43
|
+
- **Sitemap Generator**: Generate from blocks, arrays, or model collections
|
|
44
|
+
- **URL Entry**: Full sitemap.org protocol support (loc, lastmod, changefreq, priority)
|
|
45
|
+
- **Dynamic Generation**: Lambda support for dynamic attributes
|
|
46
|
+
- **File Writing**: Write sitemaps directly to files
|
|
47
|
+
- **Rails Integration**: Controller actions and Rake tasks
|
|
48
|
+
- **Validation**: Automatic URL validation (format, protocol)
|
|
49
|
+
- **Method Chaining**: Fluent interface for adding multiple URLs
|
|
50
|
+
|
|
51
|
+
- **Structured Data (JSON-LD)**
|
|
52
|
+
- **Base Class**: Generic structured data with full Schema.org support
|
|
53
|
+
- **Organization**: Company/organization information with address, social profiles
|
|
54
|
+
- **Article**: Blog posts, news articles with author, publisher, metadata
|
|
55
|
+
- **Person**: Author profiles, team members with job title, social links
|
|
56
|
+
- **Generator Helper**: Factory methods and batch script tag generation
|
|
57
|
+
- **Nested Data**: Automatic handling of complex object relationships
|
|
58
|
+
- **JSON-LD Output**: Valid Schema.org JSON-LD format
|
|
59
|
+
- **Rails Integration**: View helpers and layout integration
|
|
60
|
+
|
|
41
61
|
### 🚧 Planned
|
|
42
62
|
|
|
43
|
-
- **
|
|
44
|
-
-
|
|
45
|
-
-
|
|
63
|
+
- **Additional Structured Data Types** (v0.7.0)
|
|
64
|
+
- Product (e-commerce with price, availability)
|
|
65
|
+
- BreadcrumbList (navigation breadcrumbs)
|
|
66
|
+
- LocalBusiness (physical locations)
|
|
67
|
+
- Event (conferences, webinars)
|
|
68
|
+
- FAQPage, HowTo, Recipe
|
|
69
|
+
|
|
70
|
+
- **Advanced Generators** (v0.7.0)
|
|
71
|
+
- Breadcrumbs HTML generator
|
|
46
72
|
- AMP HTML generator
|
|
73
|
+
- Canonical URL management
|
|
47
74
|
|
|
48
|
-
- **Advanced Rails Integration** (v0.
|
|
75
|
+
- **Advanced Rails Integration** (v0.7.0)
|
|
49
76
|
- Controller helpers for setting page SEO
|
|
50
77
|
- Railtie for automatic initialization
|
|
51
78
|
- Generator for initializer file
|
|
79
|
+
- Automatic meta tags from model attributes
|
|
52
80
|
|
|
53
|
-
- **Sitemap
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
81
|
+
- **Advanced Sitemap Features** (v0.7.0)
|
|
82
|
+
- Multi-language sitemap support (hreflang)
|
|
83
|
+
- Sitemap index for large sites (50,000+ URLs)
|
|
84
|
+
- Image/video sitemap extensions
|
|
85
|
+
- News sitemap support
|
|
58
86
|
|
|
59
87
|
- **Advanced Features** (v0.6.0+)
|
|
60
88
|
- robots.txt generator
|
|
@@ -64,10 +92,12 @@ A comprehensive SEO gem for Ruby and Rails applications. BetterSeo provides a cl
|
|
|
64
92
|
|
|
65
93
|
## Installation
|
|
66
94
|
|
|
95
|
+
### For Production Use (when published to RubyGems)
|
|
96
|
+
|
|
67
97
|
Add this line to your application's Gemfile:
|
|
68
98
|
|
|
69
99
|
```ruby
|
|
70
|
-
gem 'better_seo'
|
|
100
|
+
gem 'better_seo', '~> 0.6.0'
|
|
71
101
|
```
|
|
72
102
|
|
|
73
103
|
And then execute:
|
|
@@ -82,6 +112,23 @@ Or install it yourself as:
|
|
|
82
112
|
gem install better_seo
|
|
83
113
|
```
|
|
84
114
|
|
|
115
|
+
### For Development (from source)
|
|
116
|
+
|
|
117
|
+
Add this line to your application's Gemfile:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
gem 'better_seo', git: 'https://github.com/alessiobussolari/better_seo.git', tag: 'v0.6.0'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Or clone and build locally:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/alessiobussolari/better_seo.git
|
|
127
|
+
cd better_seo
|
|
128
|
+
gem build better_seo.gemspec
|
|
129
|
+
gem install better_seo-0.6.0.gem
|
|
130
|
+
```
|
|
131
|
+
|
|
85
132
|
## Quick Start
|
|
86
133
|
|
|
87
134
|
### 1. Configuration
|
|
@@ -555,6 +602,740 @@ Then use it in your views:
|
|
|
555
602
|
og_image: url_for(@article.cover_image) %>
|
|
556
603
|
```
|
|
557
604
|
|
|
605
|
+
### 6. Sitemap Generation
|
|
606
|
+
|
|
607
|
+
BetterSeo provides a comprehensive sitemap generation system with support for XML sitemaps, dynamic content, and model collections.
|
|
608
|
+
|
|
609
|
+
#### Basic Sitemap Generation
|
|
610
|
+
|
|
611
|
+
Generate a simple sitemap using the block syntax:
|
|
612
|
+
|
|
613
|
+
```ruby
|
|
614
|
+
xml = BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
615
|
+
sitemap.add_url("https://example.com")
|
|
616
|
+
sitemap.add_url("https://example.com/about")
|
|
617
|
+
sitemap.add_url("https://example.com/contact")
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
puts xml
|
|
621
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
|
622
|
+
# <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
623
|
+
# <url>
|
|
624
|
+
# <loc>https://example.com</loc>
|
|
625
|
+
# <changefreq>weekly</changefreq>
|
|
626
|
+
# <priority>0.5</priority>
|
|
627
|
+
# </url>
|
|
628
|
+
# ...
|
|
629
|
+
# </urlset>
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### URL Entry with Full Attributes
|
|
633
|
+
|
|
634
|
+
Add URLs with all sitemap attributes (lastmod, changefreq, priority):
|
|
635
|
+
|
|
636
|
+
```ruby
|
|
637
|
+
xml = BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
638
|
+
sitemap.add_url(
|
|
639
|
+
"https://example.com",
|
|
640
|
+
lastmod: Date.today,
|
|
641
|
+
changefreq: "daily",
|
|
642
|
+
priority: 1.0
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
sitemap.add_url(
|
|
646
|
+
"https://example.com/blog",
|
|
647
|
+
lastmod: "2024-01-15",
|
|
648
|
+
changefreq: "weekly",
|
|
649
|
+
priority: 0.8
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
sitemap.add_url(
|
|
653
|
+
"https://example.com/about",
|
|
654
|
+
changefreq: "monthly",
|
|
655
|
+
priority: 0.5
|
|
656
|
+
)
|
|
657
|
+
end
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
**Valid changefreq values**: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`
|
|
661
|
+
|
|
662
|
+
**Priority range**: 0.0 to 1.0 (default: 0.5)
|
|
663
|
+
|
|
664
|
+
#### Method Chaining
|
|
665
|
+
|
|
666
|
+
The builder supports fluent method chaining:
|
|
667
|
+
|
|
668
|
+
```ruby
|
|
669
|
+
xml = BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
670
|
+
sitemap
|
|
671
|
+
.add_url("https://example.com", priority: 1.0)
|
|
672
|
+
.add_url("https://example.com/about", priority: 0.8)
|
|
673
|
+
.add_url("https://example.com/contact", priority: 0.6)
|
|
674
|
+
end
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### Generate from Array
|
|
678
|
+
|
|
679
|
+
Create a sitemap from an array of URLs:
|
|
680
|
+
|
|
681
|
+
```ruby
|
|
682
|
+
urls = [
|
|
683
|
+
"https://example.com",
|
|
684
|
+
"https://example.com/about",
|
|
685
|
+
"https://example.com/contact",
|
|
686
|
+
"https://example.com/blog"
|
|
687
|
+
]
|
|
688
|
+
|
|
689
|
+
xml = BetterSeo::Sitemap::Generator.generate_from(
|
|
690
|
+
urls,
|
|
691
|
+
changefreq: "weekly",
|
|
692
|
+
priority: 0.7
|
|
693
|
+
)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### Generate from Model Collection
|
|
697
|
+
|
|
698
|
+
Generate sitemaps dynamically from your Rails models:
|
|
699
|
+
|
|
700
|
+
```ruby
|
|
701
|
+
# Simple example with Post model
|
|
702
|
+
xml = BetterSeo::Sitemap::Generator.generate_from_collection(
|
|
703
|
+
Post.published,
|
|
704
|
+
url: ->(post) { "https://example.com/posts/#{post.slug}" },
|
|
705
|
+
lastmod: ->(post) { post.updated_at },
|
|
706
|
+
changefreq: "weekly",
|
|
707
|
+
priority: 0.8
|
|
708
|
+
)
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Dynamic attributes with lambdas**:
|
|
712
|
+
|
|
713
|
+
```ruby
|
|
714
|
+
xml = BetterSeo::Sitemap::Generator.generate_from_collection(
|
|
715
|
+
Article.all,
|
|
716
|
+
url: ->(article) { "https://example.com/articles/#{article.slug}" },
|
|
717
|
+
lastmod: ->(article) { article.updated_at },
|
|
718
|
+
changefreq: ->(article) do
|
|
719
|
+
article.featured? ? "daily" : "weekly"
|
|
720
|
+
end,
|
|
721
|
+
priority: ->(article) do
|
|
722
|
+
article.featured? ? 0.9 : 0.6
|
|
723
|
+
end
|
|
724
|
+
)
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
#### Rails Routes Integration
|
|
728
|
+
|
|
729
|
+
Generate sitemap from Rails routes:
|
|
730
|
+
|
|
731
|
+
```ruby
|
|
732
|
+
# config/routes.rb
|
|
733
|
+
Rails.application.routes.draw do
|
|
734
|
+
# Your routes...
|
|
735
|
+
|
|
736
|
+
# Sitemap endpoint
|
|
737
|
+
get '/sitemap.xml', to: 'sitemaps#index', defaults: { format: 'xml' }
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# app/controllers/sitemaps_controller.rb
|
|
741
|
+
class SitemapsController < ApplicationController
|
|
742
|
+
def index
|
|
743
|
+
@sitemap_xml = generate_sitemap
|
|
744
|
+
render xml: @sitemap_xml
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
private
|
|
748
|
+
|
|
749
|
+
def generate_sitemap
|
|
750
|
+
BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
751
|
+
# Static pages
|
|
752
|
+
sitemap.add_url(root_url, priority: 1.0, changefreq: "daily")
|
|
753
|
+
sitemap.add_url(about_url, priority: 0.8, changefreq: "monthly")
|
|
754
|
+
sitemap.add_url(contact_url, priority: 0.7, changefreq: "monthly")
|
|
755
|
+
|
|
756
|
+
# Dynamic content from models
|
|
757
|
+
Post.published.find_each do |post|
|
|
758
|
+
sitemap.add_url(
|
|
759
|
+
post_url(post),
|
|
760
|
+
lastmod: post.updated_at,
|
|
761
|
+
changefreq: "weekly",
|
|
762
|
+
priority: 0.8
|
|
763
|
+
)
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
Category.all.find_each do |category|
|
|
767
|
+
sitemap.add_url(
|
|
768
|
+
category_url(category),
|
|
769
|
+
lastmod: category.updated_at,
|
|
770
|
+
changefreq: "weekly",
|
|
771
|
+
priority: 0.7
|
|
772
|
+
)
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### Write Sitemap to File
|
|
780
|
+
|
|
781
|
+
Save sitemap directly to a file:
|
|
782
|
+
|
|
783
|
+
```ruby
|
|
784
|
+
# In a Rake task or script
|
|
785
|
+
BetterSeo::Sitemap::Generator.write_to_file('public/sitemap.xml') do |sitemap|
|
|
786
|
+
sitemap.add_url("https://example.com", priority: 1.0)
|
|
787
|
+
|
|
788
|
+
Post.published.find_each do |post|
|
|
789
|
+
sitemap.add_url(
|
|
790
|
+
"https://example.com/posts/#{post.slug}",
|
|
791
|
+
lastmod: post.updated_at,
|
|
792
|
+
changefreq: "weekly",
|
|
793
|
+
priority: 0.8
|
|
794
|
+
)
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
# Returns the file path: "public/sitemap.xml"
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### Rake Task for Sitemap Generation
|
|
802
|
+
|
|
803
|
+
Create a Rake task to regenerate your sitemap:
|
|
804
|
+
|
|
805
|
+
```ruby
|
|
806
|
+
# lib/tasks/sitemap.rake
|
|
807
|
+
namespace :sitemap do
|
|
808
|
+
desc "Generate sitemap.xml"
|
|
809
|
+
task generate: :environment do
|
|
810
|
+
file_path = BetterSeo::Sitemap::Generator.write_to_file('public/sitemap.xml') do |sitemap|
|
|
811
|
+
# Add static pages
|
|
812
|
+
sitemap.add_url("#{ENV['SITE_URL']}", priority: 1.0, changefreq: "daily")
|
|
813
|
+
sitemap.add_url("#{ENV['SITE_URL']}/about", priority: 0.8)
|
|
814
|
+
sitemap.add_url("#{ENV['SITE_URL']}/contact", priority: 0.7)
|
|
815
|
+
|
|
816
|
+
# Add dynamic content
|
|
817
|
+
Post.published.find_each do |post|
|
|
818
|
+
sitemap.add_url(
|
|
819
|
+
"#{ENV['SITE_URL']}/posts/#{post.slug}",
|
|
820
|
+
lastmod: post.updated_at,
|
|
821
|
+
changefreq: "weekly",
|
|
822
|
+
priority: 0.8
|
|
823
|
+
)
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
puts "Sitemap generated at #{file_path}"
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
# Run with: rake sitemap:generate
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
#### Using the Builder Directly
|
|
835
|
+
|
|
836
|
+
For more control, use the Builder class directly:
|
|
837
|
+
|
|
838
|
+
```ruby
|
|
839
|
+
builder = BetterSeo::Sitemap::Builder.new
|
|
840
|
+
|
|
841
|
+
# Add URLs
|
|
842
|
+
builder.add_url("https://example.com", priority: 1.0)
|
|
843
|
+
builder.add_url("https://example.com/about", priority: 0.8)
|
|
844
|
+
|
|
845
|
+
# Add multiple URLs at once
|
|
846
|
+
builder.add_urls(
|
|
847
|
+
["https://example.com/blog", "https://example.com/contact"],
|
|
848
|
+
changefreq: "weekly",
|
|
849
|
+
priority: 0.7
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
# Remove a URL
|
|
853
|
+
builder.remove_url("https://example.com/contact")
|
|
854
|
+
|
|
855
|
+
# Check size
|
|
856
|
+
puts builder.size # => 3
|
|
857
|
+
|
|
858
|
+
# Iterate over URLs
|
|
859
|
+
builder.each do |url|
|
|
860
|
+
puts "#{url.loc} - Priority: #{url.priority}"
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
# Generate XML
|
|
864
|
+
xml = builder.to_xml
|
|
865
|
+
|
|
866
|
+
# Validate all URLs
|
|
867
|
+
builder.validate! # Raises ValidationError if any URL is invalid
|
|
868
|
+
|
|
869
|
+
# Clear all URLs
|
|
870
|
+
builder.clear
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
#### URL Entry Details
|
|
874
|
+
|
|
875
|
+
Work with individual URL entries:
|
|
876
|
+
|
|
877
|
+
```ruby
|
|
878
|
+
entry = BetterSeo::Sitemap::UrlEntry.new(
|
|
879
|
+
"https://example.com/page",
|
|
880
|
+
lastmod: Date.today,
|
|
881
|
+
changefreq: "daily",
|
|
882
|
+
priority: 0.8
|
|
883
|
+
)
|
|
884
|
+
|
|
885
|
+
# Access attributes
|
|
886
|
+
entry.loc # => "https://example.com/page"
|
|
887
|
+
entry.lastmod # => "2024-01-15"
|
|
888
|
+
entry.changefreq # => "daily"
|
|
889
|
+
entry.priority # => 0.8
|
|
890
|
+
|
|
891
|
+
# Update attributes
|
|
892
|
+
entry.lastmod = Date.new(2024, 1, 20)
|
|
893
|
+
entry.changefreq = "weekly"
|
|
894
|
+
entry.priority = 0.9
|
|
895
|
+
|
|
896
|
+
# Generate XML for single entry
|
|
897
|
+
entry.to_xml
|
|
898
|
+
# <url>
|
|
899
|
+
# <loc>https://example.com/page</loc>
|
|
900
|
+
# <lastmod>2024-01-20</lastmod>
|
|
901
|
+
# <changefreq>weekly</changefreq>
|
|
902
|
+
# <priority>0.9</priority>
|
|
903
|
+
# </url>
|
|
904
|
+
|
|
905
|
+
# Convert to hash
|
|
906
|
+
entry.to_h
|
|
907
|
+
# {
|
|
908
|
+
# loc: "https://example.com/page",
|
|
909
|
+
# lastmod: "2024-01-20",
|
|
910
|
+
# changefreq: "weekly",
|
|
911
|
+
# priority: 0.9
|
|
912
|
+
# }
|
|
913
|
+
|
|
914
|
+
# Validate
|
|
915
|
+
entry.validate! # Raises ValidationError if invalid
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
#### Advanced: Multi-Model Sitemap
|
|
919
|
+
|
|
920
|
+
Combine multiple models in a single sitemap:
|
|
921
|
+
|
|
922
|
+
```ruby
|
|
923
|
+
xml = BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
924
|
+
# Homepage
|
|
925
|
+
sitemap.add_url("https://example.com", priority: 1.0, changefreq: "daily")
|
|
926
|
+
|
|
927
|
+
# Blog posts
|
|
928
|
+
Post.published.find_each do |post|
|
|
929
|
+
sitemap.add_url(
|
|
930
|
+
"https://example.com/posts/#{post.slug}",
|
|
931
|
+
lastmod: post.updated_at,
|
|
932
|
+
changefreq: post.featured? ? "daily" : "weekly",
|
|
933
|
+
priority: post.featured? ? 0.9 : 0.7
|
|
934
|
+
)
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
# Categories
|
|
938
|
+
Category.all.find_each do |category|
|
|
939
|
+
sitemap.add_url(
|
|
940
|
+
"https://example.com/categories/#{category.slug}",
|
|
941
|
+
lastmod: category.updated_at,
|
|
942
|
+
changefreq: "weekly",
|
|
943
|
+
priority: 0.6
|
|
944
|
+
)
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
# Static pages
|
|
948
|
+
%w[about contact privacy terms].each do |page|
|
|
949
|
+
sitemap.add_url(
|
|
950
|
+
"https://example.com/#{page}",
|
|
951
|
+
changefreq: "monthly",
|
|
952
|
+
priority: 0.5
|
|
953
|
+
)
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
#### Validation
|
|
959
|
+
|
|
960
|
+
All URLs are automatically validated when generating:
|
|
961
|
+
|
|
962
|
+
```ruby
|
|
963
|
+
# This will raise BetterSeo::ValidationError
|
|
964
|
+
xml = BetterSeo::Sitemap::Generator.generate do |sitemap|
|
|
965
|
+
sitemap.add_url("") # Error: Location is required
|
|
966
|
+
sitemap.add_url("not-a-valid-url") # Error: Invalid URL format
|
|
967
|
+
sitemap.add_url("ftp://example.com") # Error: Must be HTTP/HTTPS
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# Validate manually
|
|
971
|
+
builder = BetterSeo::Sitemap::Builder.new
|
|
972
|
+
builder.add_url("https://example.com")
|
|
973
|
+
builder.validate! # Returns true if all URLs valid
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
#### Complete Example: Production Sitemap
|
|
977
|
+
|
|
978
|
+
```ruby
|
|
979
|
+
# app/services/sitemap_generator_service.rb
|
|
980
|
+
class SitemapGeneratorService
|
|
981
|
+
def self.generate
|
|
982
|
+
BetterSeo::Sitemap::Generator.write_to_file(Rails.root.join('public', 'sitemap.xml')) do |sitemap|
|
|
983
|
+
add_static_pages(sitemap)
|
|
984
|
+
add_blog_posts(sitemap)
|
|
985
|
+
add_categories(sitemap)
|
|
986
|
+
add_products(sitemap) if defined?(Product)
|
|
987
|
+
end
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
private_class_method def self.add_static_pages(sitemap)
|
|
991
|
+
sitemap.add_url(Rails.application.routes.url_helpers.root_url, priority: 1.0, changefreq: "daily")
|
|
992
|
+
sitemap.add_url(Rails.application.routes.url_helpers.about_url, priority: 0.8, changefreq: "monthly")
|
|
993
|
+
sitemap.add_url(Rails.application.routes.url_helpers.contact_url, priority: 0.7, changefreq: "monthly")
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
private_class_method def self.add_blog_posts(sitemap)
|
|
997
|
+
Post.published.find_each do |post|
|
|
998
|
+
sitemap.add_url(
|
|
999
|
+
Rails.application.routes.url_helpers.post_url(post),
|
|
1000
|
+
lastmod: post.updated_at,
|
|
1001
|
+
changefreq: post.frequently_updated? ? "daily" : "weekly",
|
|
1002
|
+
priority: calculate_post_priority(post)
|
|
1003
|
+
)
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
private_class_method def self.add_categories(sitemap)
|
|
1008
|
+
Category.all.find_each do |category|
|
|
1009
|
+
sitemap.add_url(
|
|
1010
|
+
Rails.application.routes.url_helpers.category_url(category),
|
|
1011
|
+
lastmod: category.posts.maximum(:updated_at),
|
|
1012
|
+
changefreq: "weekly",
|
|
1013
|
+
priority: 0.6
|
|
1014
|
+
)
|
|
1015
|
+
end
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
private_class_method def self.add_products(sitemap)
|
|
1019
|
+
Product.available.find_each do |product|
|
|
1020
|
+
sitemap.add_url(
|
|
1021
|
+
Rails.application.routes.url_helpers.product_url(product),
|
|
1022
|
+
lastmod: product.updated_at,
|
|
1023
|
+
changefreq: "daily",
|
|
1024
|
+
priority: product.featured? ? 0.95 : 0.75
|
|
1025
|
+
)
|
|
1026
|
+
end
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
private_class_method def self.calculate_post_priority(post)
|
|
1030
|
+
base_priority = 0.7
|
|
1031
|
+
base_priority += 0.2 if post.featured?
|
|
1032
|
+
base_priority += 0.1 if post.comments_count > 10
|
|
1033
|
+
[base_priority, 1.0].min
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
# Call from rake task or controller:
|
|
1038
|
+
# SitemapGeneratorService.generate
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### 7. Structured Data (JSON-LD)
|
|
1042
|
+
|
|
1043
|
+
BetterSeo provides comprehensive support for Schema.org structured data using JSON-LD format, helping search engines better understand your content.
|
|
1044
|
+
|
|
1045
|
+
#### Basic Usage
|
|
1046
|
+
|
|
1047
|
+
Create structured data objects and generate JSON-LD script tags:
|
|
1048
|
+
|
|
1049
|
+
```ruby
|
|
1050
|
+
# Create an Organization
|
|
1051
|
+
org = BetterSeo::StructuredData::Organization.new
|
|
1052
|
+
org.name("Acme Corporation")
|
|
1053
|
+
org.url("https://www.acme.com")
|
|
1054
|
+
org.logo("https://www.acme.com/logo.png")
|
|
1055
|
+
org.description("Leading provider of innovative solutions")
|
|
1056
|
+
|
|
1057
|
+
# Generate JSON-LD script tag
|
|
1058
|
+
org.to_script_tag
|
|
1059
|
+
# <script type="application/ld+json">
|
|
1060
|
+
# {
|
|
1061
|
+
# "@context": "https://schema.org",
|
|
1062
|
+
# "@type": "Organization",
|
|
1063
|
+
# "name": "Acme Corporation",
|
|
1064
|
+
# ...
|
|
1065
|
+
# }
|
|
1066
|
+
# </script>
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
#### Available Types
|
|
1070
|
+
|
|
1071
|
+
**Organization** - Company/organization information:
|
|
1072
|
+
|
|
1073
|
+
```ruby
|
|
1074
|
+
org = BetterSeo::StructuredData::Organization.new
|
|
1075
|
+
org.name("Tech Innovations Inc")
|
|
1076
|
+
org.url("https://techinnovations.com")
|
|
1077
|
+
org.logo("https://techinnovations.com/logo.png")
|
|
1078
|
+
org.description("Innovative technology solutions")
|
|
1079
|
+
org.email("contact@techinnovations.com")
|
|
1080
|
+
org.telephone("+1-555-0100")
|
|
1081
|
+
org.address(
|
|
1082
|
+
street: "123 Tech Boulevard",
|
|
1083
|
+
city: "San Francisco",
|
|
1084
|
+
region: "CA",
|
|
1085
|
+
postal_code: "94105",
|
|
1086
|
+
country: "US"
|
|
1087
|
+
)
|
|
1088
|
+
org.same_as([
|
|
1089
|
+
"https://twitter.com/techinnovations",
|
|
1090
|
+
"https://linkedin.com/company/techinnovations"
|
|
1091
|
+
])
|
|
1092
|
+
org.founding_date("2015-03-20")
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
**Article** - Blog posts, news articles, content:
|
|
1096
|
+
|
|
1097
|
+
```ruby
|
|
1098
|
+
article = BetterSeo::StructuredData::Article.new
|
|
1099
|
+
article.headline("The Future of Web Development")
|
|
1100
|
+
article.description("An in-depth analysis of emerging trends")
|
|
1101
|
+
article.image("https://example.com/article-image.jpg")
|
|
1102
|
+
article.author("Jane Smith") # Or use Person object
|
|
1103
|
+
article.date_published("2024-01-15T09:00:00Z")
|
|
1104
|
+
article.date_modified("2024-01-20T14:30:00Z")
|
|
1105
|
+
article.url("https://example.com/articles/future-of-web-dev")
|
|
1106
|
+
article.word_count(2500)
|
|
1107
|
+
article.keywords(["Web Development", "Technology", "Trends"])
|
|
1108
|
+
article.article_section("Technology")
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
**Person** - Author profiles, team members:
|
|
1112
|
+
|
|
1113
|
+
```ruby
|
|
1114
|
+
person = BetterSeo::StructuredData::Person.new
|
|
1115
|
+
person.name("Dr. Jane Smith")
|
|
1116
|
+
person.given_name("Jane")
|
|
1117
|
+
person.family_name("Smith")
|
|
1118
|
+
person.email("jane@example.com")
|
|
1119
|
+
person.url("https://janesmith.dev")
|
|
1120
|
+
person.image("https://janesmith.dev/profile.jpg")
|
|
1121
|
+
person.job_title("Chief Technology Officer")
|
|
1122
|
+
person.telephone("+1-555-0199")
|
|
1123
|
+
person.same_as([
|
|
1124
|
+
"https://twitter.com/janesmith",
|
|
1125
|
+
"https://linkedin.com/in/janesmith"
|
|
1126
|
+
])
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
#### Method Chaining
|
|
1130
|
+
|
|
1131
|
+
All structured data classes support fluent method chaining:
|
|
1132
|
+
|
|
1133
|
+
```ruby
|
|
1134
|
+
org = BetterSeo::StructuredData::Organization.new
|
|
1135
|
+
.name("Acme Corp")
|
|
1136
|
+
.url("https://acme.com")
|
|
1137
|
+
.logo("https://acme.com/logo.png")
|
|
1138
|
+
.description("Innovation at its finest")
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
#### Nested Structured Data
|
|
1142
|
+
|
|
1143
|
+
Combine multiple structured data objects:
|
|
1144
|
+
|
|
1145
|
+
```ruby
|
|
1146
|
+
# Create publisher organization
|
|
1147
|
+
publisher = BetterSeo::StructuredData::Organization.new
|
|
1148
|
+
publisher.name("Tech Publishing Co")
|
|
1149
|
+
publisher.logo("https://techpub.com/logo.png")
|
|
1150
|
+
|
|
1151
|
+
# Create author person
|
|
1152
|
+
author = BetterSeo::StructuredData::Person.new
|
|
1153
|
+
author.name("Jane Smith")
|
|
1154
|
+
author.email("jane@techpub.com")
|
|
1155
|
+
author.url("https://janesmith.dev")
|
|
1156
|
+
|
|
1157
|
+
# Create article with nested data
|
|
1158
|
+
article = BetterSeo::StructuredData::Article.new
|
|
1159
|
+
article.headline("Introduction to Ruby on Rails")
|
|
1160
|
+
article.description("A comprehensive guide for beginners")
|
|
1161
|
+
article.image("https://example.com/rails-guide.jpg")
|
|
1162
|
+
article.author(author) # Nested Person
|
|
1163
|
+
article.publisher(publisher) # Nested Organization
|
|
1164
|
+
article.date_published("2024-01-15")
|
|
1165
|
+
|
|
1166
|
+
# Generates nested JSON-LD automatically
|
|
1167
|
+
article.to_script_tag
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
#### Using the Generator Helper
|
|
1171
|
+
|
|
1172
|
+
The Generator class provides convenient factory methods:
|
|
1173
|
+
|
|
1174
|
+
```ruby
|
|
1175
|
+
# Create with block
|
|
1176
|
+
org = BetterSeo::StructuredData::Generator.organization do |o|
|
|
1177
|
+
o.name("Acme Corp")
|
|
1178
|
+
o.url("https://acme.com")
|
|
1179
|
+
o.logo("https://acme.com/logo.png")
|
|
1180
|
+
end
|
|
1181
|
+
|
|
1182
|
+
article = BetterSeo::StructuredData::Generator.article do |a|
|
|
1183
|
+
a.headline("Amazing Article")
|
|
1184
|
+
a.author("John Doe")
|
|
1185
|
+
a.date_published("2024-01-15")
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
person = BetterSeo::StructuredData::Generator.person do |p|
|
|
1189
|
+
p.name("John Doe")
|
|
1190
|
+
p.email("john@example.com")
|
|
1191
|
+
end
|
|
1192
|
+
|
|
1193
|
+
# Generate multiple script tags at once
|
|
1194
|
+
tags = BetterSeo::StructuredData::Generator.generate_script_tags([org, article, person])
|
|
1195
|
+
# Returns all three script tags joined with newlines
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
#### Rails Integration
|
|
1199
|
+
|
|
1200
|
+
Add structured data to your Rails views:
|
|
1201
|
+
|
|
1202
|
+
```erb
|
|
1203
|
+
<%# app/views/articles/show.html.erb %>
|
|
1204
|
+
<%
|
|
1205
|
+
author = BetterSeo::StructuredData::Generator.person do |p|
|
|
1206
|
+
p.name(@article.author.name)
|
|
1207
|
+
p.email(@article.author.email)
|
|
1208
|
+
p.url(@article.author.website)
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
article_sd = BetterSeo::StructuredData::Generator.article do |a|
|
|
1212
|
+
a.headline(@article.title)
|
|
1213
|
+
a.description(@article.excerpt)
|
|
1214
|
+
a.image(url_for(@article.cover_image))
|
|
1215
|
+
a.author(author)
|
|
1216
|
+
a.date_published(@article.published_at.iso8601)
|
|
1217
|
+
a.date_modified(@article.updated_at.iso8601)
|
|
1218
|
+
a.url(article_url(@article))
|
|
1219
|
+
a.word_count(@article.word_count)
|
|
1220
|
+
a.keywords(@article.tags.pluck(:name))
|
|
1221
|
+
end
|
|
1222
|
+
%>
|
|
1223
|
+
|
|
1224
|
+
<%== article_sd.to_script_tag %>
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
Or in a helper:
|
|
1228
|
+
|
|
1229
|
+
```ruby
|
|
1230
|
+
# app/helpers/structured_data_helper.rb
|
|
1231
|
+
module StructuredDataHelper
|
|
1232
|
+
def article_structured_data(article)
|
|
1233
|
+
author = BetterSeo::StructuredData::Generator.person do |p|
|
|
1234
|
+
p.name(article.author.name)
|
|
1235
|
+
p.url(author_url(article.author))
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
article_sd = BetterSeo::StructuredData::Generator.article do |a|
|
|
1239
|
+
a.headline(article.title)
|
|
1240
|
+
a.description(article.excerpt)
|
|
1241
|
+
a.author(author)
|
|
1242
|
+
a.date_published(article.published_at.iso8601)
|
|
1243
|
+
a.url(article_url(article))
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1246
|
+
article_sd.to_script_tag.html_safe
|
|
1247
|
+
end
|
|
1248
|
+
|
|
1249
|
+
def organization_structured_data
|
|
1250
|
+
org = BetterSeo::StructuredData::Generator.organization do |o|
|
|
1251
|
+
o.name(Rails.application.config.site_name)
|
|
1252
|
+
o.url(root_url)
|
|
1253
|
+
o.logo(image_url('logo.png'))
|
|
1254
|
+
o.same_as([
|
|
1255
|
+
"https://twitter.com/yourcompany",
|
|
1256
|
+
"https://facebook.com/yourcompany"
|
|
1257
|
+
])
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1260
|
+
org.to_script_tag.html_safe
|
|
1261
|
+
end
|
|
1262
|
+
end
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
Then in your layout:
|
|
1266
|
+
|
|
1267
|
+
```erb
|
|
1268
|
+
<%# app/views/layouts/application.html.erb %>
|
|
1269
|
+
<head>
|
|
1270
|
+
...
|
|
1271
|
+
<%= organization_structured_data %>
|
|
1272
|
+
</head>
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
And in article views:
|
|
1276
|
+
|
|
1277
|
+
```erb
|
|
1278
|
+
<%# app/views/articles/show.html.erb %>
|
|
1279
|
+
<%= article_structured_data(@article) %>
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1282
|
+
#### Complete Example
|
|
1283
|
+
|
|
1284
|
+
```ruby
|
|
1285
|
+
# Create complete structured data for a blog article
|
|
1286
|
+
publisher = BetterSeo::StructuredData::Generator.organization do |o|
|
|
1287
|
+
o.name("Tech Blog Publishing")
|
|
1288
|
+
o.url("https://techblog.com")
|
|
1289
|
+
o.logo("https://techblog.com/logo.png")
|
|
1290
|
+
end
|
|
1291
|
+
|
|
1292
|
+
author = BetterSeo::StructuredData::Generator.person do |p|
|
|
1293
|
+
p.name("Dr. Sarah Johnson")
|
|
1294
|
+
p.email("sarah@techblog.com")
|
|
1295
|
+
p.url("https://sarahjohnson.dev")
|
|
1296
|
+
p.job_title("Senior Technology Writer")
|
|
1297
|
+
end
|
|
1298
|
+
|
|
1299
|
+
article = BetterSeo::StructuredData::Generator.article do |a|
|
|
1300
|
+
a.headline("The Complete Guide to Ruby on Rails in 2024")
|
|
1301
|
+
a.description("Everything you need to know about Rails")
|
|
1302
|
+
a.image([
|
|
1303
|
+
"https://techblog.com/images/rails-2024-1.jpg",
|
|
1304
|
+
"https://techblog.com/images/rails-2024-2.jpg"
|
|
1305
|
+
])
|
|
1306
|
+
a.author(author)
|
|
1307
|
+
a.publisher(publisher)
|
|
1308
|
+
a.date_published("2024-01-15T09:00:00Z")
|
|
1309
|
+
a.date_modified("2024-01-20T15:30:00Z")
|
|
1310
|
+
a.url("https://techblog.com/rails-complete-guide-2024")
|
|
1311
|
+
a.word_count(3500)
|
|
1312
|
+
a.keywords(["Ruby on Rails", "Web Development", "2024"])
|
|
1313
|
+
a.article_section("Programming")
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
# Get JSON-LD
|
|
1317
|
+
json_ld = article.to_json
|
|
1318
|
+
|
|
1319
|
+
# Get script tag for HTML
|
|
1320
|
+
script_tag = article.to_script_tag
|
|
1321
|
+
|
|
1322
|
+
# Or generate all at once
|
|
1323
|
+
all_tags = BetterSeo::StructuredData::Generator.generate_script_tags([
|
|
1324
|
+
publisher,
|
|
1325
|
+
author,
|
|
1326
|
+
article
|
|
1327
|
+
])
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
#### Benefits
|
|
1331
|
+
|
|
1332
|
+
- **SEO Enhancement**: Help search engines understand your content better
|
|
1333
|
+
- **Rich Snippets**: Enable rich results in search results (ratings, images, etc.)
|
|
1334
|
+
- **Type Safety**: Fluent API with method chaining
|
|
1335
|
+
- **Nested Data**: Automatic handling of complex relationships
|
|
1336
|
+
- **Standards Compliant**: Follows Schema.org specifications
|
|
1337
|
+
- **Easy Integration**: Works seamlessly with Rails views and helpers
|
|
1338
|
+
|
|
558
1339
|
## Configuration Reference
|
|
559
1340
|
|
|
560
1341
|
### Global Configuration
|
data/lib/better_seo/version.rb
CHANGED
data/lib/better_seo.rb
CHANGED
|
@@ -14,6 +14,14 @@ require_relative "better_seo/generators/meta_tags_generator"
|
|
|
14
14
|
require_relative "better_seo/generators/open_graph_generator"
|
|
15
15
|
require_relative "better_seo/generators/twitter_cards_generator"
|
|
16
16
|
require_relative "better_seo/rails/helpers/seo_helper"
|
|
17
|
+
require_relative "better_seo/sitemap/url_entry"
|
|
18
|
+
require_relative "better_seo/sitemap/builder"
|
|
19
|
+
require_relative "better_seo/sitemap/generator"
|
|
20
|
+
require_relative "better_seo/structured_data/base"
|
|
21
|
+
require_relative "better_seo/structured_data/organization"
|
|
22
|
+
require_relative "better_seo/structured_data/article"
|
|
23
|
+
require_relative "better_seo/structured_data/person"
|
|
24
|
+
require_relative "better_seo/structured_data/generator"
|
|
17
25
|
|
|
18
26
|
module BetterSeo
|
|
19
27
|
class << self
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_seo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- alessiobussolari
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -53,8 +53,8 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0.22'
|
|
55
55
|
description: BetterSeo provides a clean, fluent DSL for managing meta tags, Open Graph,
|
|
56
|
-
Twitter Cards, and more. Features include automatic HTML generation,
|
|
57
|
-
|
|
56
|
+
Twitter Cards, XML sitemaps, and more. Features include automatic HTML generation,
|
|
57
|
+
dynamic sitemap generation, validation, Rails integration, and 99.7% test coverage.
|
|
58
58
|
email:
|
|
59
59
|
- alessio@cosmic.tech
|
|
60
60
|
executables: []
|