better_seo 0.14.0 → 1.0.0.2

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +30 -0
  3. data/.rubocop_todo.yml +360 -0
  4. data/CHANGELOG.md +105 -0
  5. data/README.md +280 -180
  6. data/docs/00_OVERVIEW.md +472 -0
  7. data/docs/01_CORE_AND_CONFIGURATION.md +913 -0
  8. data/docs/02_META_TAGS_AND_OPEN_GRAPH.md +251 -0
  9. data/docs/03_STRUCTURED_DATA.md +140 -0
  10. data/docs/04_SITEMAP_AND_ROBOTS.md +131 -0
  11. data/docs/05_RAILS_INTEGRATION.md +175 -0
  12. data/docs/06_I18N_PAGE_GENERATOR.md +233 -0
  13. data/docs/07_IMAGE_OPTIMIZATION.md +260 -0
  14. data/docs/DEPENDENCIES.md +383 -0
  15. data/docs/README.md +180 -0
  16. data/docs/TESTING_STRATEGY.md +663 -0
  17. data/lib/better_seo/analytics/google_analytics.rb +83 -0
  18. data/lib/better_seo/analytics/google_tag_manager.rb +74 -0
  19. data/lib/better_seo/configuration.rb +322 -0
  20. data/lib/better_seo/dsl/base.rb +86 -0
  21. data/lib/better_seo/dsl/meta_tags.rb +55 -0
  22. data/lib/better_seo/dsl/open_graph.rb +105 -0
  23. data/lib/better_seo/dsl/twitter_cards.rb +129 -0
  24. data/lib/better_seo/errors.rb +31 -0
  25. data/lib/better_seo/generators/amp_generator.rb +77 -0
  26. data/lib/better_seo/generators/breadcrumbs_generator.rb +127 -0
  27. data/lib/better_seo/generators/canonical_url_manager.rb +100 -0
  28. data/lib/better_seo/generators/meta_tags_generator.rb +101 -0
  29. data/lib/better_seo/generators/open_graph_generator.rb +110 -0
  30. data/lib/better_seo/generators/robots_txt_generator.rb +96 -0
  31. data/lib/better_seo/generators/twitter_cards_generator.rb +102 -0
  32. data/lib/better_seo/image/optimizer.rb +145 -0
  33. data/lib/better_seo/rails/helpers/controller_helpers.rb +120 -0
  34. data/lib/better_seo/rails/helpers/seo_helper.rb +172 -0
  35. data/lib/better_seo/rails/helpers/structured_data_helper.rb +123 -0
  36. data/lib/better_seo/rails/model_helpers.rb +62 -0
  37. data/lib/better_seo/rails/railtie.rb +22 -0
  38. data/lib/better_seo/sitemap/builder.rb +65 -0
  39. data/lib/better_seo/sitemap/generator.rb +57 -0
  40. data/lib/better_seo/sitemap/sitemap_index.rb +73 -0
  41. data/lib/better_seo/sitemap/url_entry.rb +155 -0
  42. data/lib/better_seo/structured_data/article.rb +55 -0
  43. data/lib/better_seo/structured_data/base.rb +74 -0
  44. data/lib/better_seo/structured_data/breadcrumb_list.rb +49 -0
  45. data/lib/better_seo/structured_data/event.rb +205 -0
  46. data/lib/better_seo/structured_data/faq_page.rb +55 -0
  47. data/lib/better_seo/structured_data/generator.rb +75 -0
  48. data/lib/better_seo/structured_data/how_to.rb +96 -0
  49. data/lib/better_seo/structured_data/local_business.rb +94 -0
  50. data/lib/better_seo/structured_data/organization.rb +67 -0
  51. data/lib/better_seo/structured_data/person.rb +51 -0
  52. data/lib/better_seo/structured_data/product.rb +123 -0
  53. data/lib/better_seo/structured_data/recipe.rb +134 -0
  54. data/lib/better_seo/validators/seo_recommendations.rb +165 -0
  55. data/lib/better_seo/validators/seo_validator.rb +205 -0
  56. data/lib/better_seo/version.rb +1 -1
  57. data/lib/better_seo.rb +1 -0
  58. data/lib/generators/better_seo/install_generator.rb +21 -0
  59. data/lib/generators/better_seo/templates/README +29 -0
  60. data/lib/generators/better_seo/templates/better_seo.rb +40 -0
  61. metadata +57 -2
@@ -0,0 +1,251 @@
1
+ # Step 02: Meta Tags e Open Graph
2
+
3
+ **Versione Target**: 0.3.0
4
+ **Durata Stimata**: 2-3 settimane
5
+ **Priorità**: 🔴 CRITICA (Essential SEO)
6
+ **Dipendenze**: Step 01 completato
7
+
8
+ ---
9
+
10
+ ## Obiettivi dello Step
11
+
12
+ Implementare funzionalità SEO essenziali:
13
+
14
+ 1. ✅ Meta tags HTML (title, description, keywords, robots, canonical)
15
+ 2. ✅ Open Graph protocol completo
16
+ 3. ✅ Twitter Cards
17
+ 4. ✅ Validatori meta tags
18
+ 5. ✅ HTML generators
19
+ 6. ✅ Rails helpers base
20
+
21
+ ---
22
+
23
+ ## File da Creare
24
+
25
+ ```
26
+ lib/better_seo/dsl/meta_tags.rb
27
+ lib/better_seo/dsl/open_graph.rb
28
+ lib/better_seo/dsl/twitter_cards.rb
29
+ lib/better_seo/generators/meta_tags_generator.rb
30
+ lib/better_seo/generators/open_graph_generator.rb
31
+ lib/better_seo/generators/twitter_cards_generator.rb
32
+ lib/better_seo/validators/meta_validator.rb
33
+ lib/better_seo/rails/helpers/meta_tags_helper.rb (partial)
34
+ lib/better_seo/rails/helpers/open_graph_helper.rb (partial)
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Implementazione: DSL Meta Tags
40
+
41
+ ```ruby
42
+ # lib/better_seo/dsl/meta_tags.rb
43
+ module BetterSeo
44
+ module DSL
45
+ class MetaTags < Base
46
+ def title(value = nil)
47
+ value ? set(:title, value) : get(:title)
48
+ end
49
+
50
+ def description(value = nil)
51
+ value ? set(:description, value) : get(:description)
52
+ end
53
+
54
+ def keywords(*values)
55
+ values.any? ? set(:keywords, values.flatten) : get(:keywords)
56
+ end
57
+
58
+ def author(value = nil)
59
+ value ? set(:author, value) : get(:author)
60
+ end
61
+
62
+ def canonical(value = nil)
63
+ value ? set(:canonical, value) : get(:canonical)
64
+ end
65
+
66
+ def robots(index: true, follow: true, **options)
67
+ set(:robots, { index: index, follow: follow }.merge(options))
68
+ end
69
+
70
+ def viewport(value = "width=device-width, initial-scale=1.0")
71
+ set(:viewport, value)
72
+ end
73
+
74
+ def charset(value = "UTF-8")
75
+ set(:charset, value)
76
+ end
77
+
78
+ protected
79
+
80
+ def validate!
81
+ errors = []
82
+
83
+ if config[:title] && config[:title].length > 60
84
+ errors << "Title too long (#{config[:title].length} chars, max 60 recommended)"
85
+ end
86
+
87
+ if config[:description] && config[:description].length > 160
88
+ errors << "Description too long (#{config[:description].length} chars, max 160 recommended)"
89
+ end
90
+
91
+ raise ValidationError, errors.join(", ") if errors.any?
92
+ end
93
+ end
94
+ end
95
+ end
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Implementazione: Generator Meta Tags
101
+
102
+ ```ruby
103
+ # lib/better_seo/generators/meta_tags_generator.rb
104
+ module BetterSeo
105
+ module Generators
106
+ class MetaTagsGenerator
107
+ def initialize(config)
108
+ @config = config
109
+ end
110
+
111
+ def generate
112
+ tags = []
113
+
114
+ tags << charset_tag if @config[:charset]
115
+ tags << viewport_tag if @config[:viewport]
116
+ tags << title_tag if @config[:title]
117
+ tags << description_tag if @config[:description]
118
+ tags << keywords_tag if @config[:keywords]&.any?
119
+ tags << author_tag if @config[:author]
120
+ tags << robots_tag if @config[:robots]
121
+ tags << canonical_tag if @config[:canonical]
122
+
123
+ tags.compact.join("\n")
124
+ end
125
+
126
+ private
127
+
128
+ def charset_tag
129
+ %(<meta charset="#{escape(@config[:charset])}">)
130
+ end
131
+
132
+ def viewport_tag
133
+ %(<meta name="viewport" content="#{escape(@config[:viewport])}">)
134
+ end
135
+
136
+ def title_tag
137
+ %(<title>#{escape(@config[:title])}</title>)
138
+ end
139
+
140
+ def description_tag
141
+ %(<meta name="description" content="#{escape(@config[:description])}">)
142
+ end
143
+
144
+ def keywords_tag
145
+ keywords = Array(@config[:keywords]).join(", ")
146
+ %(<meta name="keywords" content="#{escape(keywords)}">)
147
+ end
148
+
149
+ def author_tag
150
+ %(<meta name="author" content="#{escape(@config[:author])}">)
151
+ end
152
+
153
+ def robots_tag
154
+ robots = @config[:robots]
155
+ parts = []
156
+ parts << "index" if robots[:index]
157
+ parts << "noindex" unless robots[:index]
158
+ parts << "follow" if robots[:follow]
159
+ parts << "nofollow" unless robots[:follow]
160
+
161
+ %(<meta name="robots" content="#{parts.join(", ")}">)
162
+ end
163
+
164
+ def canonical_tag
165
+ %(<link rel="canonical" href="#{escape(@config[:canonical])}">)
166
+ end
167
+
168
+ def escape(text)
169
+ text.to_s.gsub('"', '&quot;').gsub('<', '&lt;').gsub('>', '&gt;')
170
+ end
171
+ end
172
+ end
173
+ end
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Test Suite
179
+
180
+ ```ruby
181
+ # spec/dsl/meta_tags_spec.rb
182
+ RSpec.describe BetterSeo::DSL::MetaTags do
183
+ subject(:meta) { described_class.new }
184
+
185
+ it "sets title" do
186
+ meta.title "My Page Title"
187
+ expect(meta.title).to eq("My Page Title")
188
+ end
189
+
190
+ it "validates title length" do
191
+ meta.title "A" * 80
192
+ expect { meta.build }.to raise_error(BetterSeo::ValidationError, /too long/)
193
+ end
194
+
195
+ it "sets robots directives" do
196
+ meta.robots index: true, follow: false
197
+ expect(meta.robots).to eq({ index: true, follow: false })
198
+ end
199
+ end
200
+
201
+ # spec/generators/meta_tags_generator_spec.rb
202
+ RSpec.describe BetterSeo::Generators::MetaTagsGenerator do
203
+ let(:config) do
204
+ {
205
+ charset: "UTF-8",
206
+ title: "Test Page",
207
+ description: "Test description",
208
+ canonical: "https://example.com/test"
209
+ }
210
+ end
211
+
212
+ subject(:generator) { described_class.new(config) }
213
+
214
+ describe "#generate" do
215
+ it "generates HTML meta tags" do
216
+ html = generator.generate
217
+
218
+ expect(html).to include('<meta charset="UTF-8">')
219
+ expect(html).to include('<title>Test Page</title>')
220
+ expect(html).to include('name="description"')
221
+ expect(html).to include('rel="canonical"')
222
+ end
223
+
224
+ it "escapes HTML entities" do
225
+ config[:title] = 'Test <script>alert("xss")</script>'
226
+ html = generator.generate
227
+
228
+ expect(html).to include('&lt;script&gt;')
229
+ expect(html).not_to include('<script>')
230
+ end
231
+ end
232
+ end
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Checklist Completamento
238
+
239
+ - [ ] DSL MetaTags implementato e testato
240
+ - [ ] DSL OpenGraph implementato e testato
241
+ - [ ] DSL TwitterCards implementato e testato
242
+ - [ ] Generator MetaTags con HTML output
243
+ - [ ] Generator OpenGraph con property tags
244
+ - [ ] Generator TwitterCards
245
+ - [ ] Validatori per lunghezze ottimali
246
+ - [ ] Test coverage > 90%
247
+ - [ ] Integration con Step 01 Configuration
248
+
249
+ ---
250
+
251
+ **Prossimi Passi**: Step 03 - Structured Data
@@ -0,0 +1,140 @@
1
+ # Step 03: Structured Data (JSON-LD)
2
+
3
+ **Versione Target**: 0.4.0
4
+ **Durata Stimata**: 2-3 settimane
5
+ **Priorità**: 🟡 MEDIA
6
+ **Dipendenze**: Step 01
7
+
8
+ ---
9
+
10
+ ## Obiettivi
11
+
12
+ 1. ✅ Schema.org types (Article, Product, Organization, Person, Breadcrumb)
13
+ 2. ✅ JSON-LD generator
14
+ 3. ✅ DSL per structured data
15
+ 4. ✅ Schema validator
16
+ 5. ✅ Multiple structured data per page
17
+
18
+ ---
19
+
20
+ ## File da Creare
21
+
22
+ ```
23
+ lib/better_seo/schema/base.rb
24
+ lib/better_seo/schema/article.rb
25
+ lib/better_seo/schema/product.rb
26
+ lib/better_seo/schema/organization.rb
27
+ lib/better_seo/schema/person.rb
28
+ lib/better_seo/schema/breadcrumb.rb
29
+ lib/better_seo/generators/json_ld_generator.rb
30
+ lib/better_seo/dsl/structured_data.rb
31
+ lib/better_seo/validators/schema_validator.rb
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Implementazione: Schema Article
37
+
38
+ ```ruby
39
+ # lib/better_seo/schema/article.rb
40
+ module BetterSeo
41
+ module Schema
42
+ class Article < Base
43
+ def schema_type
44
+ "Article"
45
+ end
46
+
47
+ def headline(value = nil)
48
+ value ? set(:headline, value) : get(:headline)
49
+ end
50
+
51
+ def date_published(value = nil)
52
+ value ? set(:datePublished, format_date(value)) : get(:datePublished)
53
+ end
54
+
55
+ def date_modified(value = nil)
56
+ value ? set(:dateModified, format_date(value)) : get(:dateModified)
57
+ end
58
+
59
+ def author(&block)
60
+ if block_given?
61
+ person = Person.new
62
+ person.evaluate(&block)
63
+ set(:author, person.to_schema)
64
+ else
65
+ get(:author)
66
+ end
67
+ end
68
+
69
+ def publisher(&block)
70
+ if block_given?
71
+ org = Organization.new
72
+ org.evaluate(&block)
73
+ set(:publisher, org.to_schema)
74
+ else
75
+ get(:publisher)
76
+ end
77
+ end
78
+
79
+ def to_schema
80
+ {
81
+ "@context" => "https://schema.org",
82
+ "@type" => schema_type
83
+ }.merge(config)
84
+ end
85
+
86
+ private
87
+
88
+ def format_date(value)
89
+ return value if value.is_a?(String)
90
+ value.respond_to?(:iso8601) ? value.iso8601 : value.to_s
91
+ end
92
+ end
93
+ end
94
+ end
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Implementazione: JSON-LD Generator
100
+
101
+ ```ruby
102
+ # lib/better_seo/generators/json_ld_generator.rb
103
+ require "json"
104
+
105
+ module BetterSeo
106
+ module Generators
107
+ class JsonLdGenerator
108
+ def initialize(schema_data)
109
+ @schema_data = Array(schema_data)
110
+ end
111
+
112
+ def generate
113
+ return "" if @schema_data.empty?
114
+
115
+ scripts = @schema_data.map do |schema|
116
+ json = JSON.pretty_generate(schema)
117
+ %(<script type="application/ld+json">\n#{json}\n</script>)
118
+ end
119
+
120
+ scripts.join("\n")
121
+ end
122
+ end
123
+ end
124
+ end
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Checklist
130
+
131
+ - [ ] Schema base types (Article, Product, Organization, Person, Breadcrumb)
132
+ - [ ] JSON-LD generator
133
+ - [ ] DSL structured_data
134
+ - [ ] Schema validator (schema.org compliance)
135
+ - [ ] Support multiple schemas per page
136
+ - [ ] Test coverage > 90%
137
+
138
+ ---
139
+
140
+ **Prossimi Passi**: Step 04 - Sitemap & Robots
@@ -0,0 +1,131 @@
1
+ # Step 04: Sitemap XML e Robots.txt
2
+
3
+ **Versione Target**: 0.5.0
4
+ **Durata Stimata**: 1-2 settimane
5
+ **Priorità**: 🟡 MEDIA
6
+ **Dipendenze**: Step 01
7
+
8
+ ---
9
+
10
+ ## Obiettivi
11
+
12
+ 1. ✅ Sitemap XML generator
13
+ 2. ✅ Dynamic sitemap from Rails models
14
+ 3. ✅ Sitemap index (>50k URLs)
15
+ 4. ✅ Robots.txt generator
16
+ 5. ✅ Ping search engines
17
+ 6. ✅ Rake tasks
18
+
19
+ ---
20
+
21
+ ## File da Creare
22
+
23
+ ```
24
+ lib/better_seo/generators/sitemap_generator.rb
25
+ lib/better_seo/generators/robots_generator.rb
26
+ lib/better_seo/tasks/sitemap.rake
27
+ lib/better_seo/tasks/robots.rake
28
+ lib/better_seo/sitemap/url_builder.rb
29
+ lib/better_seo/sitemap/index_builder.rb
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Implementazione: Sitemap Generator
35
+
36
+ ```ruby
37
+ # lib/better_seo/generators/sitemap_generator.rb
38
+ require "builder"
39
+
40
+ module BetterSeo
41
+ module Generators
42
+ class SitemapGenerator
43
+ def initialize(urls, options = {})
44
+ @urls = urls
45
+ @options = options
46
+ end
47
+
48
+ def generate
49
+ xml = Builder::XmlMarkup.new(indent: 2)
50
+ xml.instruct! :xml, version: "1.0", encoding: "UTF-8"
51
+
52
+ xml.urlset xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9" do
53
+ @urls.each do |url_data|
54
+ xml.url do
55
+ xml.loc url_data[:loc]
56
+ xml.lastmod url_data[:lastmod] if url_data[:lastmod]
57
+ xml.changefreq url_data[:changefreq] if url_data[:changefreq]
58
+ xml.priority url_data[:priority] if url_data[:priority]
59
+ end
60
+ end
61
+ end
62
+
63
+ xml.target!
64
+ end
65
+
66
+ def save(path)
67
+ File.write(path, generate)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Rake Tasks
77
+
78
+ ```ruby
79
+ # lib/better_seo/tasks/sitemap.rake
80
+ namespace :better_seo do
81
+ namespace :sitemap do
82
+ desc "Generate sitemap.xml"
83
+ task generate: :environment do
84
+ config = BetterSeo.configuration.sitemap
85
+
86
+ urls = []
87
+
88
+ # Add static URLs
89
+ urls << { loc: "#{config.host}/", priority: 1.0, changefreq: "daily" }
90
+
91
+ # Add dynamic URLs from models (configured in better_seo.yml)
92
+ # Article.published.each do |article|
93
+ # urls << {
94
+ # loc: "#{config.host}#{article_path(article)}",
95
+ # lastmod: article.updated_at.iso8601,
96
+ # changefreq: "weekly",
97
+ # priority: 0.8
98
+ # }
99
+ # end
100
+
101
+ generator = BetterSeo::Generators::SitemapGenerator.new(urls)
102
+ generator.save(config.output_path)
103
+
104
+ puts "✓ Sitemap generated: #{config.output_path}"
105
+ end
106
+
107
+ desc "Generate and ping search engines"
108
+ task generate_and_ping: :generate do
109
+ # Ping Google, Bing, etc.
110
+ puts "✓ Pinged search engines"
111
+ end
112
+ end
113
+ end
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Checklist
119
+
120
+ - [ ] Sitemap XML generator con Builder
121
+ - [ ] Dynamic URLs from models
122
+ - [ ] Sitemap index per >50k URLs
123
+ - [ ] Robots.txt generator
124
+ - [ ] Rake tasks (generate, ping)
125
+ - [ ] Gzip compression opzionale
126
+ - [ ] Image sitemap support
127
+ - [ ] Test coverage > 85%
128
+
129
+ ---
130
+
131
+ **Prossimi Passi**: Step 05 - Rails Integration
@@ -0,0 +1,175 @@
1
+ # Step 05: Rails Integration Completa
2
+
3
+ **Versione Target**: 0.6.0
4
+ **Durata Stimata**: 2 settimane
5
+ **Priorità**: 🔴 ALTA
6
+ **Dipendenze**: Step 01, 02, 03, 04
7
+
8
+ ---
9
+
10
+ ## Obiettivi
11
+
12
+ 1. ✅ View helpers completi (meta_tags, open_graph, structured_data)
13
+ 2. ✅ Controller concerns (SeoAware)
14
+ 3. ✅ Rails generators (install, config, sitemap, robots)
15
+ 4. ✅ Rails Engine
16
+ 5. ✅ Routes integration
17
+ 6. ✅ Middleware (optional)
18
+
19
+ ---
20
+
21
+ ## File da Creare
22
+
23
+ ```
24
+ lib/better_seo/rails/helpers/meta_tags_helper.rb
25
+ lib/better_seo/rails/helpers/open_graph_helper.rb
26
+ lib/better_seo/rails/helpers/structured_data_helper.rb
27
+ lib/better_seo/rails/concerns/seo_aware.rb
28
+ lib/better_seo/rails/engine.rb
29
+ lib/generators/better_seo/install_generator.rb
30
+ lib/generators/better_seo/config_generator.rb
31
+ lib/generators/better_seo/page_generator.rb (parziale, completato in Step 06)
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Implementazione: MetaTagsHelper
37
+
38
+ ```ruby
39
+ # lib/better_seo/rails/helpers/meta_tags_helper.rb
40
+ module BetterSeo
41
+ module Rails
42
+ module Helpers
43
+ module MetaTagsHelper
44
+ def seo_meta_tags(options = {}, &block)
45
+ meta_dsl = BetterSeo::DSL::MetaTags.new
46
+
47
+ # Load defaults from configuration
48
+ config = BetterSeo.configuration.meta_tags
49
+ meta_dsl.merge!(config.to_h)
50
+
51
+ # Load from controller if set
52
+ if controller.respond_to?(:current_seo_meta) && controller.current_seo_meta
53
+ meta_dsl.merge!(controller.current_seo_meta)
54
+ end
55
+
56
+ # Apply block overrides
57
+ meta_dsl.evaluate(&block) if block_given?
58
+
59
+ # Apply options hash
60
+ meta_dsl.merge!(options) if options.any?
61
+
62
+ # Generate HTML
63
+ generator = BetterSeo::Generators::MetaTagsGenerator.new(meta_dsl.build)
64
+ generator.generate.html_safe
65
+ end
66
+
67
+ def render_seo_tags
68
+ tags = []
69
+ tags << seo_meta_tags
70
+ tags << open_graph_tags if BetterSeo.configuration.open_graph_enabled?
71
+ tags << twitter_card_tags if BetterSeo.configuration.twitter_enabled?
72
+ tags << structured_data_tags if BetterSeo.configuration.structured_data_enabled?
73
+
74
+ safe_join(tags, "\n")
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Implementazione: SeoAware Concern
85
+
86
+ ```ruby
87
+ # lib/better_seo/rails/concerns/seo_aware.rb
88
+ module BetterSeo
89
+ module Rails
90
+ module Concerns
91
+ module SeoAware
92
+ extend ActiveSupport::Concern
93
+
94
+ included do
95
+ helper_method :current_seo_meta, :current_seo_og, :current_seo_structured_data
96
+ end
97
+
98
+ def set_seo(&block)
99
+ @_seo_builder = BetterSeo::DSL::MetaTags.new
100
+ @_seo_builder.evaluate(&block) if block_given?
101
+ end
102
+
103
+ def set_seo_from_locale(page_name)
104
+ # Implementato in Step 06
105
+ end
106
+
107
+ def current_seo_meta
108
+ @_seo_builder&.to_h || {}
109
+ end
110
+
111
+ def current_seo_og
112
+ @_seo_og || {}
113
+ end
114
+
115
+ def current_seo_structured_data
116
+ @_seo_structured_data || []
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Rails Generator: Install
127
+
128
+ ```ruby
129
+ # lib/generators/better_seo/install_generator.rb
130
+ module BetterSeo
131
+ module Generators
132
+ class InstallGenerator < Rails::Generators::Base
133
+ source_root File.expand_path("templates", __dir__)
134
+
135
+ desc "Install BetterSeo in your Rails application"
136
+
137
+ def create_initializer
138
+ template "initializer.rb", "config/initializers/better_seo.rb"
139
+ end
140
+
141
+ def create_config
142
+ template "better_seo.yml", "config/better_seo.yml"
143
+ end
144
+
145
+ def create_locale_directories
146
+ empty_directory "config/locales/seo/it"
147
+ empty_directory "config/locales/seo/en"
148
+ end
149
+
150
+ def show_readme
151
+ readme "README" if behavior == :invoke
152
+ end
153
+ end
154
+ end
155
+ end
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Checklist
161
+
162
+ - [ ] View helpers (meta_tags, open_graph, structured_data)
163
+ - [ ] Helper `render_seo_tags` (all-in-one)
164
+ - [ ] Controller concern `SeoAware`
165
+ - [ ] Method `set_seo` DSL in controllers
166
+ - [ ] Rails generators (install, config)
167
+ - [ ] Generator templates
168
+ - [ ] Rails Engine per routes
169
+ - [ ] Auto-include helpers in ApplicationHelper
170
+ - [ ] Auto-include concerns in ApplicationController
171
+ - [ ] Test coverage > 90%
172
+
173
+ ---
174
+
175
+ **Prossimi Passi**: Step 06 - i18n Page Generator