better_seo 1.0.0.1 → 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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +30 -0
  3. data/.rubocop_todo.yml +360 -0
  4. data/CHANGELOG.md +44 -0
  5. data/README.md +7 -25
  6. data/lib/better_seo/analytics/google_analytics.rb +12 -12
  7. data/lib/better_seo/analytics/google_tag_manager.rb +7 -7
  8. data/lib/better_seo/configuration.rb +11 -5
  9. data/lib/better_seo/dsl/base.rb +1 -1
  10. data/lib/better_seo/dsl/open_graph.rb +2 -6
  11. data/lib/better_seo/dsl/twitter_cards.rb +5 -7
  12. data/lib/better_seo/generators/amp_generator.rb +8 -14
  13. data/lib/better_seo/generators/breadcrumbs_generator.rb +14 -13
  14. data/lib/better_seo/generators/canonical_url_manager.rb +8 -14
  15. data/lib/better_seo/generators/meta_tags_generator.rb +7 -6
  16. data/lib/better_seo/generators/open_graph_generator.rb +5 -5
  17. data/lib/better_seo/generators/robots_txt_generator.rb +5 -11
  18. data/lib/better_seo/generators/twitter_cards_generator.rb +4 -4
  19. data/lib/better_seo/image/optimizer.rb +2 -0
  20. data/lib/better_seo/rails/helpers/controller_helpers.rb +3 -1
  21. data/lib/better_seo/rails/helpers/seo_helper.rb +3 -7
  22. data/lib/better_seo/rails/helpers/structured_data_helper.rb +2 -2
  23. data/lib/better_seo/sitemap/generator.rb +1 -1
  24. data/lib/better_seo/sitemap/sitemap_index.rb +5 -5
  25. data/lib/better_seo/sitemap/url_entry.rb +9 -11
  26. data/lib/better_seo/structured_data/base.rb +2 -1
  27. data/lib/better_seo/structured_data/event.rb +1 -3
  28. data/lib/better_seo/structured_data/generator.rb +10 -10
  29. data/lib/better_seo/structured_data/recipe.rb +3 -4
  30. data/lib/better_seo/validators/seo_recommendations.rb +7 -7
  31. data/lib/better_seo/validators/seo_validator.rb +33 -23
  32. data/lib/better_seo/version.rb +1 -1
  33. data/lib/generators/better_seo/templates/better_seo.rb +2 -2
  34. metadata +3 -1
@@ -37,10 +37,8 @@ module BetterSeo
37
37
  return get(:image) if value.nil?
38
38
 
39
39
  if value.is_a?(Hash)
40
- set(:image, value)
41
- else
42
- set(:image, value)
43
40
  end
41
+ set(:image, value)
44
42
  end
45
43
 
46
44
  def image_alt(value = nil)
@@ -74,10 +72,10 @@ module BetterSeo
74
72
  else
75
73
  # Set for all platforms
76
74
  set(:app_name, {
77
- iphone: value,
78
- ipad: value,
79
- googleplay: value
80
- })
75
+ iphone: value,
76
+ ipad: value,
77
+ googleplay: value
78
+ })
81
79
  end
82
80
  end
83
81
 
@@ -32,22 +32,16 @@ module BetterSeo
32
32
  def to_meta_tags
33
33
  tags = []
34
34
 
35
- if @canonical_url
36
- tags << %(<link rel="canonical" href="#{escape_html(@canonical_url)}">)
37
- end
35
+ tags << %(<link rel="canonical" href="#{escape_html(@canonical_url)}">) if @canonical_url
38
36
 
39
- if @title
40
- tags << %(<meta property="og:title" content="#{escape_html(@title)}">)
41
- end
37
+ tags << %(<meta property="og:title" content="#{escape_html(@title)}">) if @title
42
38
 
43
39
  if @description
44
40
  tags << %(<meta name="description" content="#{escape_html(@description)}">)
45
41
  tags << %(<meta property="og:description" content="#{escape_html(@description)}">)
46
42
  end
47
43
 
48
- if @image
49
- tags << %(<meta property="og:image" content="#{escape_html(@image)}">)
50
- end
44
+ tags << %(<meta property="og:image" content="#{escape_html(@image)}">) if @image
51
45
 
52
46
  tags.join("\n")
53
47
  end
@@ -72,11 +66,11 @@ module BetterSeo
72
66
  return "" if text.nil?
73
67
 
74
68
  text.to_s
75
- .gsub("&", "&amp;")
76
- .gsub("<", "&lt;")
77
- .gsub(">", "&gt;")
78
- .gsub('"', "&quot;")
79
- .gsub("'", "&#39;")
69
+ .gsub("&", "&amp;")
70
+ .gsub("<", "&lt;")
71
+ .gsub(">", "&gt;")
72
+ .gsub('"', "&quot;")
73
+ .gsub("'", "&#39;")
80
74
  end
81
75
  end
82
76
  end
@@ -46,10 +46,13 @@ module BetterSeo
46
46
  html << %( <ol class="#{escape_html(list_class)}">)
47
47
 
48
48
  @items.each_with_index do |item, index|
49
+ active_class = item[:url].nil? ? " active" : ""
50
+ aria_current = item[:url].nil? ? ' aria-current="page"' : ""
51
+
49
52
  if schema
50
- html << %( <li class="breadcrumb-item#{item[:url].nil? ? " active" : ""}" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"#{item[:url].nil? ? ' aria-current="page"' : ""}>)
53
+ html << %( <li class="breadcrumb-item#{active_class}" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"#{aria_current}>)
51
54
  else
52
- html << %( <li class="breadcrumb-item#{item[:url].nil? ? " active" : ""}"#{item[:url].nil? ? ' aria-current="page"' : ""}>)
55
+ html << %( <li class="breadcrumb-item#{active_class}"#{aria_current}>)
53
56
  end
54
57
 
55
58
  if item[:url]
@@ -61,13 +64,11 @@ module BetterSeo
61
64
  else
62
65
  html << %( <a href="#{escape_html(item[:url])}">#{escape_html(item[:name])}</a>)
63
66
  end
67
+ elsif schema
68
+ html << %( <span itemprop="name">#{escape_html(item[:name])}</span>)
69
+ html << %( <meta itemprop="position" content="#{index + 1}" />)
64
70
  else
65
- if schema
66
- html << %( <span itemprop="name">#{escape_html(item[:name])}</span>)
67
- html << %( <meta itemprop="position" content="#{index + 1}" />)
68
- else
69
- html << %( #{escape_html(item[:name])})
70
- end
71
+ html << %( #{escape_html(item[:name])})
71
72
  end
72
73
 
73
74
  html << %( </li>)
@@ -115,11 +116,11 @@ module BetterSeo
115
116
  return "" if text.nil?
116
117
 
117
118
  text.to_s
118
- .gsub("&", "&amp;")
119
- .gsub("<", "&lt;")
120
- .gsub(">", "&gt;")
121
- .gsub('"', "&quot;")
122
- .gsub("'", "&#39;")
119
+ .gsub("&", "&amp;")
120
+ .gsub("<", "&lt;")
121
+ .gsub(">", "&gt;")
122
+ .gsub('"', "&quot;")
123
+ .gsub("'", "&#39;")
123
124
  end
124
125
  end
125
126
  end
@@ -42,14 +42,10 @@ module BetterSeo
42
42
  uri = URI.parse(@url)
43
43
 
44
44
  # Check if it's a relative URL
45
- unless uri.absolute?
46
- raise ValidationError, "Canonical URL must be absolute: #{@url}"
47
- end
45
+ raise ValidationError, "Canonical URL must be absolute: #{@url}" unless uri.absolute?
48
46
 
49
47
  # Check if it's HTTP or HTTPS
50
- unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
51
- raise ValidationError, "Invalid URL format: #{@url}"
52
- end
48
+ raise ValidationError, "Invalid URL format: #{@url}" unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
53
49
  rescue URI::InvalidURIError
54
50
  raise ValidationError, "Invalid URL format: #{@url}"
55
51
  end
@@ -81,9 +77,7 @@ module BetterSeo
81
77
  normalized = uri.to_s
82
78
 
83
79
  # Remove trailing slash (except for root URL)
84
- if normalized.end_with?("/") && normalized.count("/") > 3
85
- normalized = normalized[0...-1]
86
- end
80
+ normalized = normalized[0...-1] if normalized.end_with?("/") && normalized.count("/") > 3
87
81
 
88
82
  # Lowercase if configured
89
83
  normalized = normalized.downcase if @lowercase
@@ -95,11 +89,11 @@ module BetterSeo
95
89
  return "" if text.nil?
96
90
 
97
91
  text.to_s
98
- .gsub("&", "&amp;")
99
- .gsub("<", "&lt;")
100
- .gsub(">", "&gt;")
101
- .gsub('"', "&quot;")
102
- .gsub("'", "&#39;")
92
+ .gsub("&", "&amp;")
93
+ .gsub("<", "&lt;")
94
+ .gsub(">", "&gt;")
95
+ .gsub('"', "&quot;")
96
+ .gsub("'", "&#39;")
103
97
  end
104
98
  end
105
99
  end
@@ -47,7 +47,7 @@ module BetterSeo
47
47
 
48
48
  def keywords_tag
49
49
  keywords = @config[:keywords]
50
- return nil unless keywords && keywords.any?
50
+ return nil unless keywords&.any?
51
51
 
52
52
  keywords_str = Array(keywords).join(", ")
53
53
  %(<meta name="keywords" content="#{escape(keywords_str)}">)
@@ -79,7 +79,8 @@ module BetterSeo
79
79
 
80
80
  # Additional directives
81
81
  robots.each do |key, value|
82
- next if [:index, :follow].include?(key)
82
+ next if %i[index follow].include?(key)
83
+
83
84
  directives << key.to_s if value
84
85
  end
85
86
 
@@ -90,10 +91,10 @@ module BetterSeo
90
91
 
91
92
  def escape(text)
92
93
  text.to_s
93
- .gsub("&", "&amp;")
94
- .gsub('"', "&quot;")
95
- .gsub("<", "&lt;")
96
- .gsub(">", "&gt;")
94
+ .gsub("&", "&amp;")
95
+ .gsub('"', "&quot;")
96
+ .gsub("<", "&lt;")
97
+ .gsub(">", "&gt;")
97
98
  end
98
99
  end
99
100
  end
@@ -52,7 +52,7 @@ module BetterSeo
52
52
 
53
53
  def locale_alternate_tags
54
54
  alternates = @config[:locale_alternate]
55
- return [] unless alternates && alternates.any?
55
+ return [] unless alternates&.any?
56
56
 
57
57
  Array(alternates).map do |locale|
58
58
  meta_tag("og:locale:alternate", locale)
@@ -100,10 +100,10 @@ module BetterSeo
100
100
 
101
101
  def escape(text)
102
102
  text.to_s
103
- .gsub("&", "&amp;")
104
- .gsub('"', "&quot;")
105
- .gsub("<", "&lt;")
106
- .gsub(">", "&gt;")
103
+ .gsub("&", "&amp;")
104
+ .gsub('"', "&quot;")
105
+ .gsub("<", "&lt;")
106
+ .gsub(">", "&gt;")
107
107
  end
108
108
  end
109
109
  end
@@ -58,23 +58,17 @@ module BetterSeo
58
58
  lines << "User-agent: #{rule[:user_agent]}"
59
59
 
60
60
  # Allow directives
61
- if rule[:allow]
62
- rule[:allow].each do |path|
63
- lines << "Allow: #{path}"
64
- end
61
+ rule[:allow]&.each do |path|
62
+ lines << "Allow: #{path}"
65
63
  end
66
64
 
67
65
  # Disallow directives
68
- if rule[:disallow]
69
- rule[:disallow].each do |path|
70
- lines << "Disallow: #{path}"
71
- end
66
+ rule[:disallow]&.each do |path|
67
+ lines << "Disallow: #{path}"
72
68
  end
73
69
 
74
70
  # Crawl delay
75
- if rule[:crawl_delay]
76
- lines << "Crawl-delay: #{rule[:crawl_delay]}"
77
- end
71
+ lines << "Crawl-delay: #{rule[:crawl_delay]}" if rule[:crawl_delay]
78
72
 
79
73
  # Add blank line between user agent sections (except last)
80
74
  lines << "" if index < @rules.size - 1
@@ -92,10 +92,10 @@ module BetterSeo
92
92
 
93
93
  def escape(text)
94
94
  text.to_s
95
- .gsub("&", "&amp;")
96
- .gsub('"', "&quot;")
97
- .gsub("<", "&lt;")
98
- .gsub(">", "&gt;")
95
+ .gsub("&", "&amp;")
96
+ .gsub('"', "&quot;")
97
+ .gsub("<", "&lt;")
98
+ .gsub(">", "&gt;")
99
99
  end
100
100
  end
101
101
  end
@@ -24,6 +24,7 @@ module BetterSeo
24
24
  unless SUPPORTED_FORMATS.include?(ext)
25
25
  raise ImageError, "Unsupported format: #{ext}. Supported: #{SUPPORTED_FORMATS.join(", ")}"
26
26
  end
27
+
27
28
  true
28
29
  end
29
30
 
@@ -136,6 +137,7 @@ module BetterSeo
136
137
 
137
138
  def validate_source!(source)
138
139
  raise ImageError, "Source file not found: #{source}" unless File.exist?(source)
140
+
139
141
  validate_format!(source)
140
142
  end
141
143
  end
@@ -62,9 +62,11 @@ module BetterSeo
62
62
  def set_page_description(description, max_length: nil)
63
63
  final_description = description
64
64
 
65
+ # rubocop:disable Style/IfUnlessModifier
65
66
  if max_length && description.length > max_length
66
- final_description = description[0...max_length - 3] + "..."
67
+ final_description = "#{description[0...(max_length - 3)]}..."
67
68
  end
69
+ # rubocop:enable Style/IfUnlessModifier
68
70
 
69
71
  merge_seo_data(:meta, { description: final_description })
70
72
  end
@@ -41,7 +41,7 @@ module BetterSeo
41
41
  end
42
42
 
43
43
  # Generate all SEO tags (meta + og + twitter) from configuration or block
44
- def seo_tags(config = nil, &block)
44
+ def seo_tags(config = nil)
45
45
  if block_given?
46
46
  context = SeoTagsContext.new
47
47
  yield(context)
@@ -55,9 +55,7 @@ module BetterSeo
55
55
 
56
56
  tags = []
57
57
 
58
- if merged_config[:meta]
59
- tags << seo_meta_tags(merged_config[:meta])
60
- end
58
+ tags << seo_meta_tags(merged_config[:meta]) if merged_config[:meta]
61
59
 
62
60
  if merged_config[:og] && BetterSeo.configuration.open_graph_enabled?
63
61
  tags << seo_open_graph_tags(merged_config[:og])
@@ -81,9 +79,7 @@ module BetterSeo
81
79
  meta = {}
82
80
 
83
81
  # Start with controller data
84
- if controller_data[:meta]
85
- meta = controller_data[:meta].dup
86
- end
82
+ meta = controller_data[:meta].dup if controller_data[:meta]
87
83
 
88
84
  # Use default title if not set
89
85
  meta[:title] ||= config.meta_tags.default_title
@@ -27,7 +27,7 @@ module BetterSeo
27
27
  raw(sd_object.to_script_tag)
28
28
  end
29
29
 
30
- def structured_data_tags(objects = nil, &block)
30
+ def structured_data_tags(objects = nil)
31
31
  array = block_given? ? yield : objects
32
32
  return "" if array.nil? || array.empty?
33
33
 
@@ -109,7 +109,7 @@ module BetterSeo
109
109
 
110
110
  private
111
111
 
112
- def create_structured_data(type, properties, &block)
112
+ def create_structured_data(type, properties)
113
113
  klass = TYPE_MAPPING[type]
114
114
  raise ArgumentError, "Unknown structured data type: #{type}" unless klass
115
115
 
@@ -6,7 +6,7 @@ module BetterSeo
6
6
  module Sitemap
7
7
  class Generator
8
8
  class << self
9
- def generate(&block)
9
+ def generate
10
10
  builder = Builder.new
11
11
  yield(builder) if block_given?
12
12
  builder.to_xml
@@ -62,11 +62,11 @@ module BetterSeo
62
62
 
63
63
  def escape_xml(text)
64
64
  text.to_s
65
- .gsub("&", "&amp;")
66
- .gsub("<", "&lt;")
67
- .gsub(">", "&gt;")
68
- .gsub('"', "&quot;")
69
- .gsub("'", "&apos;")
65
+ .gsub("&", "&amp;")
66
+ .gsub("<", "&lt;")
67
+ .gsub(">", "&gt;")
68
+ .gsub('"', "&quot;")
69
+ .gsub("'", "&apos;")
70
70
  end
71
71
  end
72
72
  end
@@ -28,13 +28,13 @@ module BetterSeo
28
28
  unless VALID_CHANGEFREQ.include?(value)
29
29
  raise ValidationError, "Invalid changefreq: #{value}. Must be one of: #{VALID_CHANGEFREQ.join(", ")}"
30
30
  end
31
+
31
32
  @changefreq = value
32
33
  end
33
34
 
34
35
  def priority=(value)
35
- unless value.between?(0.0, 1.0)
36
- raise ValidationError, "Priority must be between 0.0 and 1.0"
37
- end
36
+ raise ValidationError, "Priority must be between 0.0 and 1.0" unless value.between?(0.0, 1.0)
37
+
38
38
  @priority = value
39
39
  end
40
40
 
@@ -119,9 +119,7 @@ module BetterSeo
119
119
 
120
120
  begin
121
121
  uri = URI.parse(@loc)
122
- unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
123
- raise ValidationError, "Invalid URL format: #{@loc}"
124
- end
122
+ raise ValidationError, "Invalid URL format: #{@loc}" unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
125
123
  rescue URI::InvalidURIError
126
124
  raise ValidationError, "Invalid URL format: #{@loc}"
127
125
  end
@@ -146,11 +144,11 @@ module BetterSeo
146
144
 
147
145
  def escape_xml(text)
148
146
  text.to_s
149
- .gsub("&", "&amp;")
150
- .gsub("<", "&lt;")
151
- .gsub(">", "&gt;")
152
- .gsub('"', "&quot;")
153
- .gsub("'", "&apos;")
147
+ .gsub("&", "&amp;")
148
+ .gsub("<", "&lt;")
149
+ .gsub(">", "&gt;")
150
+ .gsub('"', "&quot;")
151
+ .gsub("'", "&apos;")
154
152
  end
155
153
  end
156
154
  end
@@ -35,7 +35,7 @@ module BetterSeo
35
35
  hash
36
36
  end
37
37
 
38
- def to_json(*args)
38
+ def to_json(*_args)
39
39
  JSON.pretty_generate(to_h)
40
40
  end
41
41
 
@@ -53,6 +53,7 @@ module BetterSeo
53
53
 
54
54
  def validate!
55
55
  raise ValidationError, "@type is required for structured data" unless valid?
56
+
56
57
  true
57
58
  end
58
59
 
@@ -82,9 +82,7 @@ module BetterSeo
82
82
  location_hash["name"] = name if name
83
83
  location_hash["url"] = url if url
84
84
 
85
- if address
86
- location_hash["address"] = build_address(address)
87
- end
85
+ location_hash["address"] = build_address(address) if address
88
86
 
89
87
  other_properties.each do |key, value|
90
88
  location_hash[key.to_s] = value
@@ -10,61 +10,61 @@ module BetterSeo
10
10
  structured_data_array.map(&:to_script_tag).join("\n\n")
11
11
  end
12
12
 
13
- def organization(**properties, &block)
13
+ def organization(**properties)
14
14
  org = Organization.new(**properties)
15
15
  yield(org) if block_given?
16
16
  org
17
17
  end
18
18
 
19
- def article(**properties, &block)
19
+ def article(**properties)
20
20
  article = Article.new(**properties)
21
21
  yield(article) if block_given?
22
22
  article
23
23
  end
24
24
 
25
- def person(**properties, &block)
25
+ def person(**properties)
26
26
  person = Person.new(**properties)
27
27
  yield(person) if block_given?
28
28
  person
29
29
  end
30
30
 
31
- def product(**properties, &block)
31
+ def product(**properties)
32
32
  product = Product.new(**properties)
33
33
  yield(product) if block_given?
34
34
  product
35
35
  end
36
36
 
37
- def breadcrumb_list(**properties, &block)
37
+ def breadcrumb_list(**properties)
38
38
  breadcrumb = BreadcrumbList.new(**properties)
39
39
  yield(breadcrumb) if block_given?
40
40
  breadcrumb
41
41
  end
42
42
 
43
- def local_business(**properties, &block)
43
+ def local_business(**properties)
44
44
  business = LocalBusiness.new(**properties)
45
45
  yield(business) if block_given?
46
46
  business
47
47
  end
48
48
 
49
- def event(**properties, &block)
49
+ def event(**properties)
50
50
  event = Event.new(**properties)
51
51
  yield(event) if block_given?
52
52
  event
53
53
  end
54
54
 
55
- def faq_page(**properties, &block)
55
+ def faq_page(**properties)
56
56
  faq = FAQPage.new(**properties)
57
57
  yield(faq) if block_given?
58
58
  faq
59
59
  end
60
60
 
61
- def how_to(**properties, &block)
61
+ def how_to(**properties)
62
62
  how_to = HowTo.new(**properties)
63
63
  yield(how_to) if block_given?
64
64
  how_to
65
65
  end
66
66
 
67
- def recipe(**properties, &block)
67
+ def recipe(**properties)
68
68
  recipe = Recipe.new(**properties)
69
69
  yield(recipe) if block_given?
70
70
  recipe
@@ -79,7 +79,8 @@ module BetterSeo
79
79
  end
80
80
 
81
81
  # Nutrition information
82
- def nutrition(calories: nil, fat_content: nil, carbohydrate_content: nil, protein_content: nil, sugar_content: nil, **other)
82
+ def nutrition(calories: nil, fat_content: nil, carbohydrate_content: nil, protein_content: nil,
83
+ sugar_content: nil, **other)
83
84
  nutrition_hash = {
84
85
  "@type" => "NutritionInformation"
85
86
  }
@@ -114,9 +115,7 @@ module BetterSeo
114
115
  def to_h
115
116
  hash = super
116
117
 
117
- if @ingredients.any?
118
- hash["recipeIngredient"] = @ingredients
119
- end
118
+ hash["recipeIngredient"] = @ingredients if @ingredients.any?
120
119
 
121
120
  if @instructions.any?
122
121
  hash["recipeInstructions"] = @instructions.map.with_index(1) do |instruction, index|
@@ -22,7 +22,7 @@ module BetterSeo
22
22
  recommendations = []
23
23
  length = title_result[:length]
24
24
 
25
- if length == 0
25
+ if length.zero?
26
26
  recommendations << {
27
27
  category: "Title",
28
28
  priority: :high,
@@ -56,7 +56,7 @@ module BetterSeo
56
56
  recommendations = []
57
57
  length = desc_result[:length]
58
58
 
59
- if length == 0
59
+ if length.zero?
60
60
  recommendations << {
61
61
  category: "Meta Description",
62
62
  priority: :high,
@@ -90,7 +90,7 @@ module BetterSeo
90
90
  recommendations = []
91
91
  h1_count = headings_result[:h1_count]
92
92
 
93
- if h1_count == 0
93
+ if h1_count.zero?
94
94
  recommendations << {
95
95
  category: "Headings",
96
96
  priority: :high,
@@ -106,7 +106,7 @@ module BetterSeo
106
106
  }
107
107
  end
108
108
 
109
- if headings_result[:total_headings] == 0
109
+ if headings_result[:total_headings].zero?
110
110
  recommendations << {
111
111
  category: "Headings",
112
112
  priority: :medium,
@@ -123,13 +123,13 @@ module BetterSeo
123
123
 
124
124
  recommendations = []
125
125
 
126
- if images_result[:images_without_alt] > 0
126
+ if images_result[:images_without_alt].positive?
127
127
  count = images_result[:images_without_alt]
128
128
  recommendations << {
129
129
  category: "Images",
130
130
  priority: :medium,
131
131
  action: "Add alt text to images",
132
- details: "#{count} image#{count > 1 ? 's' : ''} missing alt text. Alt text improves accessibility and SEO."
132
+ details: "#{count} image#{"s" if count > 1} missing alt text. Alt text improves accessibility and SEO."
133
133
  }
134
134
  end
135
135
 
@@ -145,7 +145,7 @@ module BetterSeo
145
145
  # Group by priority
146
146
  grouped = recommendations.group_by { |r| r[:priority] }
147
147
 
148
- [:high, :medium, :low].each do |priority|
148
+ %i[high medium low].each do |priority|
149
149
  next unless grouped[priority]
150
150
 
151
151
  output << "## #{priority.to_s.capitalize} Priority\n"