better_seo 0.1.0 → 0.4.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 +63 -2
- data/README.md +699 -13
- data/lib/better_seo/version.rb +1 -1
- data/lib/better_seo.rb +38 -2
- metadata +49 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21564a3e273f4c6274fcafaddc9dc0b9e5e36524c1ca2a548ae48e455490034f
|
|
4
|
+
data.tar.gz: d82e2faf6d565f8be930ca24e9c3af7b60de23c8ed02dbcc0d14390c6725580b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 656e243f281d61d4afac21dfdd2f18be27659ae695bc5d6770146fc7e234135a21e221847157d75bb50b1d41d2798094e87868fc8d91cac1eee0fe38e651c6eb
|
|
7
|
+
data.tar.gz: b65f741d51b2b192c2456356081ad908145d68f93d37e64c6ba5d45c3ae838820120d7c852592dd60d1f3da54e2bf21e88316bede5c2e9971d0af24d2cecbe50
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
1
8
|
## [Unreleased]
|
|
2
9
|
|
|
3
|
-
## [0.
|
|
10
|
+
## [0.4.0] - 2025-01-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Rails view helpers for easy integration
|
|
14
|
+
- `seo_meta_tags` - Generate HTML meta tags
|
|
15
|
+
- `seo_open_graph_tags` - Generate Open Graph tags
|
|
16
|
+
- `seo_twitter_tags` - Generate Twitter Card tags
|
|
17
|
+
- `seo_tags` - Generate all SEO tags at once
|
|
18
|
+
- Support for both hash configuration and DSL blocks in helpers
|
|
19
|
+
- Automatic HTML safety with `raw` helper
|
|
20
|
+
- Controller integration patterns and examples
|
|
21
|
+
- Complete Rails integration documentation
|
|
22
|
+
|
|
23
|
+
### Test Coverage
|
|
24
|
+
- 286 tests passing
|
|
25
|
+
- 100% code coverage (562/562 lines)
|
|
26
|
+
|
|
27
|
+
## [0.3.0] - 2025-01-23
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- HTML Generators for converting DSL to HTML tags
|
|
31
|
+
- `MetaTagsGenerator` - Converts meta tags DSL to HTML
|
|
32
|
+
- `OpenGraphGenerator` - Converts Open Graph DSL to HTML
|
|
33
|
+
- `TwitterCardsGenerator` - Converts Twitter Cards DSL to HTML
|
|
34
|
+
- HTML entity escaping for security (XSS prevention)
|
|
35
|
+
|
|
36
|
+
### Test Coverage
|
|
37
|
+
- 271 tests passing
|
|
38
|
+
- 100% code coverage (505/505 lines)
|
|
39
|
+
|
|
40
|
+
## [0.2.0] - 2025-01-22
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- DSL Builders for creating SEO configurations
|
|
44
|
+
- `DSL::MetaTags` - For HTML meta tags
|
|
45
|
+
- `DSL::OpenGraph` - For Open Graph protocol
|
|
46
|
+
- `DSL::TwitterCards` - For Twitter Cards
|
|
47
|
+
- Fluent interface with method chaining
|
|
48
|
+
- Automatic validation for all DSL builders
|
|
49
|
+
|
|
50
|
+
### Test Coverage
|
|
51
|
+
- 175 tests passing
|
|
52
|
+
- 100% code coverage (327/327 lines)
|
|
53
|
+
|
|
54
|
+
## [0.1.0] - 2025-01-22
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- Core configuration system with singleton pattern
|
|
58
|
+
- Nested configuration objects
|
|
59
|
+
- Feature flags for enabling/disabling modules
|
|
60
|
+
- Validation with detailed error messages
|
|
61
|
+
- i18n support with multiple locales
|
|
62
|
+
- Custom error classes
|
|
4
63
|
|
|
5
|
-
|
|
64
|
+
### Test Coverage
|
|
65
|
+
- 74 tests passing
|
|
66
|
+
- 100% code coverage (179/179 lines)
|
data/README.md
CHANGED
|
@@ -1,38 +1,710 @@
|
|
|
1
1
|
# BetterSeo
|
|
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
|
-
|
|
5
|
+
[](https://github.com/yourusername/better_seo)
|
|
6
|
+
[](https://github.com/yourusername/better_seo)
|
|
7
|
+
[](https://www.ruby-lang.org)
|
|
8
|
+
[](https://rubyonrails.org)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
### ✅ Implemented (v0.2.0)
|
|
13
|
+
|
|
14
|
+
- **Core Configuration System**
|
|
15
|
+
- Singleton configuration with block-style setup
|
|
16
|
+
- Nested configuration objects
|
|
17
|
+
- Feature flags for enabling/disabling modules
|
|
18
|
+
- Validation with detailed error messages
|
|
19
|
+
- i18n support with multiple locales
|
|
20
|
+
|
|
21
|
+
- **DSL Builders**
|
|
22
|
+
- **Meta Tags DSL**: title, description, keywords, author, canonical, robots, viewport, charset
|
|
23
|
+
- **Open Graph DSL**: Complete OG protocol support including articles, images, videos, audio
|
|
24
|
+
- **Twitter Cards DSL**: All card types (summary, summary_large_image, app, player)
|
|
25
|
+
- Fluent interface with method chaining
|
|
26
|
+
- Automatic validation (title/description length, required fields)
|
|
27
|
+
|
|
28
|
+
- **HTML Generators**
|
|
29
|
+
- **MetaTagsGenerator**: Converts DSL to HTML meta tags
|
|
30
|
+
- **OpenGraphGenerator**: Converts DSL to Open Graph meta tags
|
|
31
|
+
- **TwitterCardsGenerator**: Converts DSL to Twitter Card meta tags
|
|
32
|
+
- HTML entity escaping for security
|
|
33
|
+
- Integration with DSL builders
|
|
34
|
+
|
|
35
|
+
- **Rails Integration**
|
|
36
|
+
- **View Helpers**: `seo_meta_tags`, `seo_open_graph_tags`, `seo_twitter_tags`, `seo_tags`
|
|
37
|
+
- Support for hash configuration and DSL blocks
|
|
38
|
+
- Automatic HTML safety with `raw` helper
|
|
39
|
+
- Integration with global configuration defaults
|
|
40
|
+
|
|
41
|
+
### 🚧 Planned
|
|
42
|
+
|
|
43
|
+
- **Advanced Generators** (v0.5.0)
|
|
44
|
+
- Schema.org JSON-LD generator
|
|
45
|
+
- Breadcrumbs generator
|
|
46
|
+
- AMP HTML generator
|
|
47
|
+
|
|
48
|
+
- **Advanced Rails Integration** (v0.5.0)
|
|
49
|
+
- Controller helpers for setting page SEO
|
|
50
|
+
- Railtie for automatic initialization
|
|
51
|
+
- Generator for initializer file
|
|
52
|
+
|
|
53
|
+
- **Sitemap Generation** (v0.5.0)
|
|
54
|
+
- XML sitemap builder
|
|
55
|
+
- Automatic Rails routes integration
|
|
56
|
+
- Multi-language sitemap support
|
|
57
|
+
- Sitemap index for large sites
|
|
58
|
+
|
|
59
|
+
- **Advanced Features** (v0.6.0+)
|
|
60
|
+
- robots.txt generator
|
|
61
|
+
- Image optimization with WebP conversion
|
|
62
|
+
- Structured data builders (Organization, Person, Product, etc.)
|
|
63
|
+
- SEO validators and recommendations
|
|
6
64
|
|
|
7
65
|
## Installation
|
|
8
66
|
|
|
9
|
-
|
|
67
|
+
Add this line to your application's Gemfile:
|
|
10
68
|
|
|
11
|
-
|
|
69
|
+
```ruby
|
|
70
|
+
gem 'better_seo'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
And then execute:
|
|
12
74
|
|
|
13
75
|
```bash
|
|
14
|
-
bundle
|
|
76
|
+
bundle install
|
|
15
77
|
```
|
|
16
78
|
|
|
17
|
-
|
|
79
|
+
Or install it yourself as:
|
|
18
80
|
|
|
19
81
|
```bash
|
|
20
|
-
gem install
|
|
82
|
+
gem install better_seo
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
### 1. Configuration
|
|
88
|
+
|
|
89
|
+
Create an initializer (Rails) or configure at app startup:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# config/initializers/better_seo.rb
|
|
93
|
+
BetterSeo.configure do |config|
|
|
94
|
+
config.site_name = "My Awesome Site"
|
|
95
|
+
config.default_locale = :en
|
|
96
|
+
config.available_locales = [:en, :it, :fr]
|
|
97
|
+
|
|
98
|
+
# Configure defaults for meta tags
|
|
99
|
+
config.meta_tags.default_title = "My Awesome Site"
|
|
100
|
+
config.meta_tags.title_separator = " | "
|
|
101
|
+
config.meta_tags.append_site_name = true
|
|
102
|
+
config.meta_tags.default_description = "The best site on the internet"
|
|
103
|
+
config.meta_tags.default_keywords = ["awesome", "site", "seo"]
|
|
104
|
+
|
|
105
|
+
# Configure Open Graph defaults
|
|
106
|
+
config.open_graph.site_name = "My Awesome Site"
|
|
107
|
+
config.open_graph.default_type = "website"
|
|
108
|
+
config.open_graph.default_locale = "en_US"
|
|
109
|
+
|
|
110
|
+
# Configure Twitter Cards defaults
|
|
111
|
+
config.twitter.site = "@mysite"
|
|
112
|
+
config.twitter.creator = "@myhandle"
|
|
113
|
+
config.twitter.card_type = "summary_large_image"
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 2. Using DSL Builders
|
|
118
|
+
|
|
119
|
+
#### Meta Tags
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
meta = BetterSeo::DSL::MetaTags.new
|
|
123
|
+
|
|
124
|
+
meta.evaluate do
|
|
125
|
+
title "My Page Title"
|
|
126
|
+
description "This is an amazing page about Ruby and SEO"
|
|
127
|
+
keywords "ruby", "seo", "meta tags"
|
|
128
|
+
author "John Doe"
|
|
129
|
+
canonical "https://example.com/my-page"
|
|
130
|
+
robots index: true, follow: true, noarchive: true
|
|
131
|
+
viewport # uses default: "width=device-width, initial-scale=1.0"
|
|
132
|
+
charset # uses default: "UTF-8"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Get the configuration
|
|
136
|
+
config = meta.build
|
|
137
|
+
# => {
|
|
138
|
+
# title: "My Page Title",
|
|
139
|
+
# description: "This is an amazing page...",
|
|
140
|
+
# keywords: ["ruby", "seo", "meta tags"],
|
|
141
|
+
# ...
|
|
142
|
+
# }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Open Graph
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
og = BetterSeo::DSL::OpenGraph.new
|
|
149
|
+
|
|
150
|
+
og.evaluate do
|
|
151
|
+
title "My OG Title"
|
|
152
|
+
description "Description for social media"
|
|
153
|
+
type "article"
|
|
154
|
+
url "https://example.com/article"
|
|
155
|
+
image "https://example.com/image.jpg"
|
|
156
|
+
site_name "My Site"
|
|
157
|
+
locale "en_US"
|
|
158
|
+
locale_alternate "it_IT", "fr_FR"
|
|
159
|
+
|
|
160
|
+
# For article type
|
|
161
|
+
article do
|
|
162
|
+
author "John Doe"
|
|
163
|
+
published_time "2024-01-01T00:00:00Z"
|
|
164
|
+
modified_time "2024-01-02T00:00:00Z"
|
|
165
|
+
section "Technology"
|
|
166
|
+
tag "Ruby", "SEO", "OpenGraph"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
config = og.build
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Twitter Cards
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
twitter = BetterSeo::DSL::TwitterCards.new
|
|
177
|
+
|
|
178
|
+
twitter.evaluate do
|
|
179
|
+
card "summary_large_image"
|
|
180
|
+
site "@mysite" # @ prefix added automatically
|
|
181
|
+
creator "myhandle" # @ prefix added automatically
|
|
182
|
+
title "Twitter Card Title"
|
|
183
|
+
description "Description for Twitter"
|
|
184
|
+
image "https://example.com/twitter-image.jpg"
|
|
185
|
+
image_alt "Image description for accessibility"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
config = twitter.build
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Method Chaining
|
|
192
|
+
|
|
193
|
+
All DSL builders support fluent interface:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
meta = BetterSeo::DSL::MetaTags.new
|
|
197
|
+
.title("Chained Title")
|
|
198
|
+
.description("Chained Description")
|
|
199
|
+
.keywords("ruby", "rails", "seo")
|
|
200
|
+
.author("Jane Doe")
|
|
201
|
+
.canonical("https://example.com/page")
|
|
202
|
+
|
|
203
|
+
og = BetterSeo::DSL::OpenGraph.new
|
|
204
|
+
.title("OG Title")
|
|
205
|
+
.type("article")
|
|
206
|
+
.url("https://example.com")
|
|
207
|
+
.image("https://example.com/og.jpg")
|
|
208
|
+
|
|
209
|
+
twitter = BetterSeo::DSL::TwitterCards.new
|
|
210
|
+
.card("summary_large_image")
|
|
211
|
+
.site("@mysite")
|
|
212
|
+
.title("Twitter Title")
|
|
213
|
+
.description("Twitter Description")
|
|
214
|
+
.image("https://example.com/twitter.jpg")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 3. HTML Generation
|
|
218
|
+
|
|
219
|
+
Once you've built your SEO configuration with DSL builders, use generators to convert them to HTML tags:
|
|
220
|
+
|
|
221
|
+
#### Meta Tags Generator
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
# Build configuration with DSL
|
|
225
|
+
meta = BetterSeo::DSL::MetaTags.new
|
|
226
|
+
meta.title("My Page Title")
|
|
227
|
+
meta.description("Page description for SEO")
|
|
228
|
+
meta.keywords("ruby", "seo", "rails")
|
|
229
|
+
meta.canonical("https://example.com/page")
|
|
230
|
+
meta.robots(index: true, follow: true)
|
|
231
|
+
|
|
232
|
+
# Generate HTML tags
|
|
233
|
+
generator = BetterSeo::Generators::MetaTagsGenerator.new(meta.build)
|
|
234
|
+
html = generator.generate
|
|
235
|
+
|
|
236
|
+
# Output:
|
|
237
|
+
# <meta charset="UTF-8">
|
|
238
|
+
# <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
239
|
+
# <title>My Page Title</title>
|
|
240
|
+
# <meta name="description" content="Page description for SEO">
|
|
241
|
+
# <meta name="keywords" content="ruby, seo, rails">
|
|
242
|
+
# <link rel="canonical" href="https://example.com/page">
|
|
243
|
+
# <meta name="robots" content="index, follow">
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### Open Graph Generator
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
# Build configuration with DSL
|
|
250
|
+
og = BetterSeo::DSL::OpenGraph.new
|
|
251
|
+
og.title("Article Title")
|
|
252
|
+
og.type("article")
|
|
253
|
+
og.url("https://example.com/article")
|
|
254
|
+
og.image(url: "https://example.com/og.jpg", width: 1200, height: 630)
|
|
255
|
+
|
|
256
|
+
# Generate HTML tags
|
|
257
|
+
generator = BetterSeo::Generators::OpenGraphGenerator.new(og.build)
|
|
258
|
+
html = generator.generate
|
|
259
|
+
|
|
260
|
+
# Output:
|
|
261
|
+
# <meta property="og:title" content="Article Title">
|
|
262
|
+
# <meta property="og:type" content="article">
|
|
263
|
+
# <meta property="og:url" content="https://example.com/article">
|
|
264
|
+
# <meta property="og:image" content="https://example.com/og.jpg">
|
|
265
|
+
# <meta property="og:image:width" content="1200">
|
|
266
|
+
# <meta property="og:image:height" content="630">
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Twitter Cards Generator
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
# Build configuration with DSL
|
|
273
|
+
twitter = BetterSeo::DSL::TwitterCards.new
|
|
274
|
+
twitter.card("summary_large_image")
|
|
275
|
+
twitter.site("@mysite")
|
|
276
|
+
twitter.title("Twitter Card Title")
|
|
277
|
+
twitter.description("Description for Twitter")
|
|
278
|
+
twitter.image("https://example.com/twitter.jpg")
|
|
279
|
+
|
|
280
|
+
# Generate HTML tags
|
|
281
|
+
generator = BetterSeo::Generators::TwitterCardsGenerator.new(twitter.build)
|
|
282
|
+
html = generator.generate
|
|
283
|
+
|
|
284
|
+
# Output:
|
|
285
|
+
# <meta name="twitter:card" content="summary_large_image">
|
|
286
|
+
# <meta name="twitter:site" content="@mysite">
|
|
287
|
+
# <meta name="twitter:title" content="Twitter Card Title">
|
|
288
|
+
# <meta name="twitter:description" content="Description for Twitter">
|
|
289
|
+
# <meta name="twitter:image" content="https://example.com/twitter.jpg">
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Complete Example
|
|
293
|
+
|
|
294
|
+
```ruby
|
|
295
|
+
# Build all SEO tags for a page
|
|
296
|
+
meta = BetterSeo::DSL::MetaTags.new.evaluate do
|
|
297
|
+
title "My Awesome Page"
|
|
298
|
+
description "This page is about Ruby SEO"
|
|
299
|
+
keywords "ruby", "seo", "meta tags"
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
og = BetterSeo::DSL::OpenGraph.new.evaluate do
|
|
303
|
+
title "My Awesome Page"
|
|
304
|
+
type "article"
|
|
305
|
+
url "https://example.com/page"
|
|
306
|
+
image "https://example.com/og.jpg"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
twitter = BetterSeo::DSL::TwitterCards.new.evaluate do
|
|
310
|
+
card "summary_large_image"
|
|
311
|
+
site "@mysite"
|
|
312
|
+
title "My Awesome Page"
|
|
313
|
+
image "https://example.com/twitter.jpg"
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Generate all HTML
|
|
317
|
+
meta_html = BetterSeo::Generators::MetaTagsGenerator.new(meta.build).generate
|
|
318
|
+
og_html = BetterSeo::Generators::OpenGraphGenerator.new(og.build).generate
|
|
319
|
+
twitter_html = BetterSeo::Generators::TwitterCardsGenerator.new(twitter.build).generate
|
|
320
|
+
|
|
321
|
+
# Combine and render in your view
|
|
322
|
+
all_tags = [meta_html, og_html, twitter_html].join("\n")
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### Security Features
|
|
326
|
+
|
|
327
|
+
All generators automatically escape HTML entities to prevent XSS attacks:
|
|
328
|
+
|
|
329
|
+
```ruby
|
|
330
|
+
meta = BetterSeo::DSL::MetaTags.new
|
|
331
|
+
meta.title('Title with "quotes" & <script>alert("xss")</script>')
|
|
332
|
+
|
|
333
|
+
generator = BetterSeo::Generators::MetaTagsGenerator.new(meta.build)
|
|
334
|
+
html = generator.generate
|
|
335
|
+
|
|
336
|
+
# Output:
|
|
337
|
+
# <title>Title with "quotes" & <script>alert("xss")</script></title>
|
|
338
|
+
# All dangerous characters are properly escaped
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 4. Validation
|
|
342
|
+
|
|
343
|
+
All DSL builders include automatic validation:
|
|
344
|
+
|
|
345
|
+
```ruby
|
|
346
|
+
meta = BetterSeo::DSL::MetaTags.new
|
|
347
|
+
meta.title("A" * 80) # Too long (max 60 chars recommended)
|
|
348
|
+
meta.build
|
|
349
|
+
# => BetterSeo::ValidationError: Title too long (80 chars, max 60 recommended)
|
|
350
|
+
|
|
351
|
+
og = BetterSeo::DSL::OpenGraph.new
|
|
352
|
+
og.title("Title")
|
|
353
|
+
og.build
|
|
354
|
+
# => BetterSeo::ValidationError: og:type is required, og:image is required, og:url is required
|
|
355
|
+
|
|
356
|
+
twitter = BetterSeo::DSL::TwitterCards.new
|
|
357
|
+
twitter.card("invalid_type")
|
|
358
|
+
twitter.build
|
|
359
|
+
# => BetterSeo::ValidationError: Invalid card type: invalid_type. Valid types: summary, summary_large_image, app, player
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 5. Rails Integration
|
|
363
|
+
|
|
364
|
+
BetterSeo provides view helpers for easy integration in Rails applications.
|
|
365
|
+
|
|
366
|
+
#### Setup
|
|
367
|
+
|
|
368
|
+
Include the helpers in your `ApplicationHelper`:
|
|
369
|
+
|
|
370
|
+
```ruby
|
|
371
|
+
# app/helpers/application_helper.rb
|
|
372
|
+
module ApplicationHelper
|
|
373
|
+
include BetterSeo::Rails::Helpers::SeoHelper
|
|
374
|
+
end
|
|
21
375
|
```
|
|
22
376
|
|
|
23
|
-
|
|
377
|
+
Or include them globally in `ApplicationController`:
|
|
24
378
|
|
|
25
|
-
|
|
379
|
+
```ruby
|
|
380
|
+
# app/controllers/application_controller.rb
|
|
381
|
+
class ApplicationController < ActionController::Base
|
|
382
|
+
helper BetterSeo::Rails::Helpers::SeoHelper
|
|
383
|
+
end
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### Using View Helpers
|
|
387
|
+
|
|
388
|
+
##### Single Tag Group Helpers
|
|
389
|
+
|
|
390
|
+
```erb
|
|
391
|
+
<%# app/views/layouts/application.html.erb %>
|
|
392
|
+
<head>
|
|
393
|
+
<%= seo_meta_tags do |meta|
|
|
394
|
+
meta.title "My Page Title"
|
|
395
|
+
meta.description "Page description"
|
|
396
|
+
meta.keywords "ruby", "rails", "seo"
|
|
397
|
+
meta.canonical request.original_url
|
|
398
|
+
meta.robots index: true, follow: true
|
|
399
|
+
end %>
|
|
400
|
+
|
|
401
|
+
<%= seo_open_graph_tags do |og|
|
|
402
|
+
og.title "My Page Title"
|
|
403
|
+
og.type "website"
|
|
404
|
+
og.url request.original_url
|
|
405
|
+
og.image image_url("og-image.jpg")
|
|
406
|
+
og.site_name "My Site"
|
|
407
|
+
end %>
|
|
408
|
+
|
|
409
|
+
<%= seo_twitter_tags do |twitter|
|
|
410
|
+
twitter.card "summary_large_image"
|
|
411
|
+
twitter.site "@mysite"
|
|
412
|
+
twitter.title "My Page Title"
|
|
413
|
+
twitter.description "Page description"
|
|
414
|
+
twitter.image image_url("twitter-image.jpg")
|
|
415
|
+
end %>
|
|
416
|
+
</head>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
##### All-in-One Helper
|
|
420
|
+
|
|
421
|
+
```erb
|
|
422
|
+
<%# Generate all SEO tags at once %>
|
|
423
|
+
<head>
|
|
424
|
+
<%= seo_tags do |seo|
|
|
425
|
+
seo.meta do |meta|
|
|
426
|
+
meta.title @page_title || "Default Title"
|
|
427
|
+
meta.description @page_description
|
|
428
|
+
meta.keywords @page_keywords if @page_keywords
|
|
429
|
+
meta.canonical request.original_url
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
seo.og do |og|
|
|
433
|
+
og.title @page_title || "Default Title"
|
|
434
|
+
og.type "article"
|
|
435
|
+
og.url request.original_url
|
|
436
|
+
og.image @og_image || image_url("default-og.jpg")
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
seo.twitter do |twitter|
|
|
440
|
+
twitter.card "summary_large_image"
|
|
441
|
+
twitter.site "@mysite"
|
|
442
|
+
twitter.title @page_title || "Default Title"
|
|
443
|
+
twitter.image @twitter_image || image_url("default-twitter.jpg")
|
|
444
|
+
end
|
|
445
|
+
end %>
|
|
446
|
+
</head>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
#### Controller Integration
|
|
450
|
+
|
|
451
|
+
Set SEO data in your controllers:
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
class ArticlesController < ApplicationController
|
|
455
|
+
def show
|
|
456
|
+
@article = Article.find(params[:id])
|
|
457
|
+
|
|
458
|
+
# Set SEO variables for the view
|
|
459
|
+
@page_title = @article.title
|
|
460
|
+
@page_description = @article.excerpt
|
|
461
|
+
@page_keywords = @article.tags.pluck(:name)
|
|
462
|
+
@og_image = url_for(@article.cover_image) if @article.cover_image.attached?
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Then use them in your layout:
|
|
468
|
+
|
|
469
|
+
```erb
|
|
470
|
+
<head>
|
|
471
|
+
<%= seo_tags do |seo|
|
|
472
|
+
seo.meta do |meta|
|
|
473
|
+
meta.title @page_title if @page_title
|
|
474
|
+
meta.description @page_description if @page_description
|
|
475
|
+
meta.keywords(*@page_keywords) if @page_keywords
|
|
476
|
+
meta.canonical request.original_url
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
seo.og do |og|
|
|
480
|
+
og.title @page_title || "Default Title"
|
|
481
|
+
og.type "article"
|
|
482
|
+
og.url request.original_url
|
|
483
|
+
og.image @og_image if @og_image
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
seo.twitter do |twitter|
|
|
487
|
+
twitter.card "summary_large_image"
|
|
488
|
+
twitter.title @page_title || "Default Title"
|
|
489
|
+
twitter.description @page_description if @page_description
|
|
490
|
+
end
|
|
491
|
+
end %>
|
|
492
|
+
</head>
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### Hash Configuration
|
|
496
|
+
|
|
497
|
+
You can also pass hash configurations directly:
|
|
498
|
+
|
|
499
|
+
```erb
|
|
500
|
+
<%= seo_meta_tags(
|
|
501
|
+
title: "My Page",
|
|
502
|
+
description: "Description",
|
|
503
|
+
keywords: ["ruby", "rails"]
|
|
504
|
+
) %>
|
|
505
|
+
|
|
506
|
+
<%= seo_open_graph_tags(
|
|
507
|
+
title: "My Page",
|
|
508
|
+
type: "article",
|
|
509
|
+
url: request.original_url,
|
|
510
|
+
image: image_url("og.jpg")
|
|
511
|
+
) %>
|
|
512
|
+
|
|
513
|
+
<%= seo_twitter_tags(
|
|
514
|
+
card: "summary",
|
|
515
|
+
title: "My Page",
|
|
516
|
+
description: "Description"
|
|
517
|
+
) %>
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
#### Partial Integration
|
|
521
|
+
|
|
522
|
+
Create reusable SEO partials:
|
|
523
|
+
|
|
524
|
+
```erb
|
|
525
|
+
<%# app/views/shared/_seo.html.erb %>
|
|
526
|
+
<%= seo_tags do |seo|
|
|
527
|
+
seo.meta do |meta|
|
|
528
|
+
meta.title local_assigns[:title] || "Default Title"
|
|
529
|
+
meta.description local_assigns[:description]
|
|
530
|
+
meta.canonical request.original_url
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
seo.og do |og|
|
|
534
|
+
og.title local_assigns[:title] || "Default Title"
|
|
535
|
+
og.type local_assigns[:og_type] || "website"
|
|
536
|
+
og.url request.original_url
|
|
537
|
+
og.image local_assigns[:og_image] || image_url("default-og.jpg")
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
seo.twitter do |twitter|
|
|
541
|
+
twitter.card "summary_large_image"
|
|
542
|
+
twitter.title local_assigns[:title] || "Default Title"
|
|
543
|
+
end
|
|
544
|
+
end %>
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Then use it in your views:
|
|
548
|
+
|
|
549
|
+
```erb
|
|
550
|
+
<%# app/views/articles/show.html.erb %>
|
|
551
|
+
<%= render "shared/seo",
|
|
552
|
+
title: @article.title,
|
|
553
|
+
description: @article.excerpt,
|
|
554
|
+
og_type: "article",
|
|
555
|
+
og_image: url_for(@article.cover_image) %>
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## Configuration Reference
|
|
559
|
+
|
|
560
|
+
### Global Configuration
|
|
561
|
+
|
|
562
|
+
```ruby
|
|
563
|
+
BetterSeo.configure do |config|
|
|
564
|
+
# Site-wide settings
|
|
565
|
+
config.site_name = "My Site"
|
|
566
|
+
config.default_locale = :en
|
|
567
|
+
config.available_locales = [:en, :it, :fr, :de, :es]
|
|
568
|
+
|
|
569
|
+
# Meta tags configuration
|
|
570
|
+
config.meta_tags.default_title = "Default Title"
|
|
571
|
+
config.meta_tags.title_separator = " | "
|
|
572
|
+
config.meta_tags.append_site_name = true
|
|
573
|
+
config.meta_tags.default_description = "Default description"
|
|
574
|
+
config.meta_tags.default_keywords = ["keyword1", "keyword2"]
|
|
575
|
+
config.meta_tags.default_author = "Your Name"
|
|
576
|
+
|
|
577
|
+
# Open Graph configuration
|
|
578
|
+
config.open_graph.enabled = true
|
|
579
|
+
config.open_graph.site_name = "My Site"
|
|
580
|
+
config.open_graph.default_type = "website"
|
|
581
|
+
config.open_graph.default_locale = "en_US"
|
|
582
|
+
config.open_graph.default_image.url = "https://example.com/default-og.jpg"
|
|
583
|
+
config.open_graph.default_image.width = 1200
|
|
584
|
+
config.open_graph.default_image.height = 630
|
|
585
|
+
|
|
586
|
+
# Twitter Cards configuration
|
|
587
|
+
config.twitter.enabled = true
|
|
588
|
+
config.twitter.site = "@mysite"
|
|
589
|
+
config.twitter.creator = "@myhandle"
|
|
590
|
+
config.twitter.card_type = "summary_large_image"
|
|
591
|
+
|
|
592
|
+
# Structured Data configuration
|
|
593
|
+
config.structured_data.enabled = true
|
|
594
|
+
config.structured_data.organization = {
|
|
595
|
+
name: "My Organization",
|
|
596
|
+
url: "https://example.com"
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
# Sitemap configuration (planned)
|
|
600
|
+
config.sitemap.enabled = false
|
|
601
|
+
config.sitemap.host = "https://example.com"
|
|
602
|
+
config.sitemap.output_path = "public/sitemap.xml"
|
|
603
|
+
|
|
604
|
+
# Robots.txt configuration (planned)
|
|
605
|
+
config.robots.enabled = false
|
|
606
|
+
config.robots.output_path = "public/robots.txt"
|
|
607
|
+
|
|
608
|
+
# Image optimization configuration (planned)
|
|
609
|
+
config.images.enabled = false
|
|
610
|
+
config.images.webp.enabled = true
|
|
611
|
+
config.images.webp.quality = 80
|
|
612
|
+
end
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### Checking Configuration
|
|
616
|
+
|
|
617
|
+
```ruby
|
|
618
|
+
# Access configuration
|
|
619
|
+
BetterSeo.configuration.site_name
|
|
620
|
+
# => "My Site"
|
|
621
|
+
|
|
622
|
+
# Check if features are enabled
|
|
623
|
+
BetterSeo.enabled?(:open_graph)
|
|
624
|
+
# => true
|
|
625
|
+
|
|
626
|
+
BetterSeo.enabled?(:sitemap)
|
|
627
|
+
# => false
|
|
628
|
+
|
|
629
|
+
# Reset configuration (useful for testing)
|
|
630
|
+
BetterSeo.reset_configuration!
|
|
631
|
+
```
|
|
26
632
|
|
|
27
633
|
## Development
|
|
28
634
|
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
635
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
# Install dependencies
|
|
639
|
+
bundle install
|
|
640
|
+
|
|
641
|
+
# Run tests
|
|
642
|
+
bundle exec rspec
|
|
643
|
+
|
|
644
|
+
# Run tests with coverage
|
|
645
|
+
bundle exec rspec --format documentation
|
|
646
|
+
|
|
647
|
+
# Check code coverage
|
|
648
|
+
open coverage/index.html
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### Running Tests
|
|
652
|
+
|
|
653
|
+
The gem uses RSpec with SimpleCov for test coverage. We maintain **100% code coverage**.
|
|
654
|
+
|
|
655
|
+
```bash
|
|
656
|
+
# Run all tests
|
|
657
|
+
bundle exec rspec
|
|
658
|
+
|
|
659
|
+
# Run specific test file
|
|
660
|
+
bundle exec rspec spec/dsl/meta_tags_spec.rb
|
|
661
|
+
|
|
662
|
+
# Run with documentation format
|
|
663
|
+
bundle exec rspec --format documentation
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Current test statistics:
|
|
667
|
+
- **286 tests** passing
|
|
668
|
+
- **100% code coverage** (562/562 lines)
|
|
669
|
+
- **3 DSL builders** fully tested
|
|
670
|
+
- **3 HTML generators** fully tested
|
|
671
|
+
- **1 Rails view helper module** fully tested
|
|
672
|
+
- **1 core configuration system** fully tested
|
|
673
|
+
|
|
674
|
+
## Architecture
|
|
30
675
|
|
|
31
|
-
|
|
676
|
+
```
|
|
677
|
+
lib/better_seo/
|
|
678
|
+
├── version.rb # Gem version
|
|
679
|
+
├── errors.rb # Custom error classes
|
|
680
|
+
├── configuration.rb # Main configuration class
|
|
681
|
+
├── dsl/
|
|
682
|
+
│ ├── base.rb # Base DSL builder class
|
|
683
|
+
│ ├── meta_tags.rb # Meta tags DSL
|
|
684
|
+
│ ├── open_graph.rb # Open Graph DSL
|
|
685
|
+
│ └── twitter_cards.rb # Twitter Cards DSL
|
|
686
|
+
├── generators/
|
|
687
|
+
│ ├── meta_tags_generator.rb # HTML meta tags generator
|
|
688
|
+
│ ├── open_graph_generator.rb # Open Graph tags generator
|
|
689
|
+
│ └── twitter_cards_generator.rb # Twitter Cards generator
|
|
690
|
+
├── rails/
|
|
691
|
+
│ └── helpers/
|
|
692
|
+
│ └── seo_helper.rb # Rails view helpers
|
|
693
|
+
└── (planned)
|
|
694
|
+
├── validators/ # SEO validators
|
|
695
|
+
└── sitemap/ # Sitemap generation
|
|
696
|
+
```
|
|
32
697
|
|
|
33
698
|
## Contributing
|
|
34
699
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
700
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/better_seo.
|
|
701
|
+
|
|
702
|
+
1. Fork it
|
|
703
|
+
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
|
704
|
+
3. Write tests (we maintain 100% coverage)
|
|
705
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
|
706
|
+
5. Push to the branch (`git push origin feature/my-new-feature`)
|
|
707
|
+
6. Create new Pull Request
|
|
36
708
|
|
|
37
709
|
## License
|
|
38
710
|
|
|
@@ -40,4 +712,18 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
40
712
|
|
|
41
713
|
## Code of Conduct
|
|
42
714
|
|
|
43
|
-
Everyone interacting in the BetterSeo project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
|
715
|
+
Everyone interacting in the BetterSeo project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yourusername/better_seo/blob/main/CODE_OF_CONDUCT.md).
|
|
716
|
+
|
|
717
|
+
## Roadmap
|
|
718
|
+
|
|
719
|
+
See [docs/00_OVERVIEW.md](docs/00_OVERVIEW.md) for the complete implementation roadmap.
|
|
720
|
+
|
|
721
|
+
### Version History
|
|
722
|
+
|
|
723
|
+
- **v0.1.0** - Core configuration system
|
|
724
|
+
- **v0.2.0** - DSL builders (Meta Tags, Open Graph, Twitter Cards)
|
|
725
|
+
- **v0.3.0** - HTML generators (Meta Tags, Open Graph, Twitter Cards)
|
|
726
|
+
- **v0.4.0** - Rails view helpers integration ← **Current**
|
|
727
|
+
- **v0.5.0** - Sitemap generation (planned)
|
|
728
|
+
- **v0.6.0** - Advanced features (planned)
|
|
729
|
+
- **v1.0.0** - Stable release (planned)
|
data/lib/better_seo/version.rb
CHANGED
data/lib/better_seo.rb
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_support/core_ext"
|
|
5
|
+
|
|
3
6
|
require_relative "better_seo/version"
|
|
7
|
+
require_relative "better_seo/errors"
|
|
8
|
+
require_relative "better_seo/configuration"
|
|
9
|
+
require_relative "better_seo/dsl/base"
|
|
10
|
+
require_relative "better_seo/dsl/meta_tags"
|
|
11
|
+
require_relative "better_seo/dsl/open_graph"
|
|
12
|
+
require_relative "better_seo/dsl/twitter_cards"
|
|
13
|
+
require_relative "better_seo/generators/meta_tags_generator"
|
|
14
|
+
require_relative "better_seo/generators/open_graph_generator"
|
|
15
|
+
require_relative "better_seo/generators/twitter_cards_generator"
|
|
16
|
+
require_relative "better_seo/rails/helpers/seo_helper"
|
|
4
17
|
|
|
5
18
|
module BetterSeo
|
|
6
|
-
class
|
|
7
|
-
|
|
19
|
+
class << self
|
|
20
|
+
# Accesso alla configurazione singleton
|
|
21
|
+
def configuration
|
|
22
|
+
@configuration ||= Configuration.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Block-style configuration
|
|
26
|
+
def configure
|
|
27
|
+
yield(configuration) if block_given?
|
|
28
|
+
configuration.validate! if block_given?
|
|
29
|
+
configuration
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Reset configuration (utile per test)
|
|
33
|
+
def reset_configuration!
|
|
34
|
+
@configuration = Configuration.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Shortcut per check feature enabled
|
|
38
|
+
def enabled?(feature)
|
|
39
|
+
configuration.public_send("#{feature}_enabled?")
|
|
40
|
+
rescue NoMethodError
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
8
44
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,60 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_seo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.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-
|
|
12
|
-
dependencies:
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
date: 2025-10-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: simplecov
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.22'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.22'
|
|
55
|
+
description: BetterSeo provides a clean, fluent DSL for managing meta tags, Open Graph,
|
|
56
|
+
Twitter Cards, and more. Features include automatic HTML generation, validation,
|
|
57
|
+
Rails view helpers, and 100% test coverage.
|
|
15
58
|
email:
|
|
16
59
|
- alessio@cosmic.tech
|
|
17
60
|
executables: []
|
|
@@ -53,5 +96,5 @@ requirements: []
|
|
|
53
96
|
rubygems_version: 3.5.11
|
|
54
97
|
signing_key:
|
|
55
98
|
specification_version: 4
|
|
56
|
-
summary:
|
|
99
|
+
summary: Comprehensive SEO gem for Ruby and Rails applications
|
|
57
100
|
test_files: []
|