indexmap 0.6.0 → 0.7.1

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.
@@ -4,18 +4,15 @@ module Indexmap
4
4
  class Writer
5
5
  VALID_FORMATS = %i[index single_file].freeze
6
6
 
7
- def initialize(public_path:, base_url:, sections: nil, entries: nil, index_filename: "sitemap.xml", format: :index)
7
+ def initialize(base_url:, sections: nil, entries: nil, index_filename: "sitemap.xml", format: :index)
8
8
  @entries = normalize_entries(entries)
9
9
  @format = normalize_format(format)
10
10
  @sections = normalize_sections(sections)
11
- @public_path = Pathname(public_path)
12
11
  @base_url = base_url
13
12
  @index_filename = index_filename
14
13
  end
15
14
 
16
15
  def write
17
- FileUtils.mkdir_p(public_path)
18
-
19
16
  return [write_file(index_filename, urlset_xml(entries))] if single_file?
20
17
 
21
18
  paths = sections.map do |section|
@@ -25,8 +22,6 @@ module Indexmap
25
22
  paths + [write_file(index_filename, index_xml(sections))]
26
23
  end
27
24
 
28
- attr_accessor :public_path
29
-
30
25
  private
31
26
 
32
27
  attr_reader :base_url, :entries, :format, :index_filename, :sections
@@ -58,9 +53,7 @@ module Indexmap
58
53
  end
59
54
 
60
55
  def write_file(filename, body)
61
- path = public_path.join(filename)
62
- path.write(body)
63
- path
56
+ Storage::File.new(filename: filename, body: body, content_type: "application/xml")
64
57
  end
65
58
 
66
59
  def urlset_xml(entries)
data/lib/indexmap.rb CHANGED
@@ -8,11 +8,14 @@ require "time"
8
8
  require_relative "indexmap/version"
9
9
  require_relative "indexmap/google_configuration"
10
10
  require_relative "indexmap/index_now_configuration"
11
+ require_relative "indexmap/storage/file"
12
+ require_relative "indexmap/storage/memory"
13
+ require_relative "indexmap/storage/filesystem"
14
+ require_relative "indexmap/storage/active_storage"
11
15
  require_relative "indexmap/configuration"
12
16
  require_relative "indexmap/creator"
13
17
  require_relative "indexmap/entry"
14
18
  require_relative "indexmap/output"
15
- require_relative "indexmap/path"
16
19
  require_relative "indexmap/parser"
17
20
  require_relative "indexmap/pinger/base"
18
21
  require_relative "indexmap/pinger/google"
@@ -5,8 +5,12 @@ namespace :indexmap do
5
5
  runner = Indexmap::TaskRunner.new
6
6
  create_result = runner.create
7
7
 
8
- puts "Created, formatted, and validated #{file_count(create_result[:files])} in #{public_directory(runner)}."
9
- puts "IndexNow key file: #{create_result[:index_now_key_path]}" if create_result[:index_now_key_path]
8
+ puts "Created, formatted, and validated #{file_count(create_result[:files])}."
9
+ puts "Files created:"
10
+ create_result[:sitemaps].each do |sitemap|
11
+ puts " - #{sitemap[:location]} (#{link_count(sitemap[:link_count])})"
12
+ end
13
+ puts "IndexNow key file: #{create_result[:index_now_key_filename]}" if create_result[:index_now_key_filename]
10
14
  end
11
15
 
12
16
  desc "Format sitemap files for better readability"
@@ -14,7 +18,7 @@ namespace :indexmap do
14
18
  runner = Indexmap::TaskRunner.new
15
19
  formatted_files = runner.format
16
20
 
17
- puts "Formatted #{file_count(formatted_files)} in #{public_directory(runner)}."
21
+ puts "Formatted #{file_count(formatted_files)} in #{storage_description(runner)}."
18
22
  end
19
23
 
20
24
  desc "Validate sitemap shape and URL hygiene"
@@ -87,8 +91,12 @@ namespace :indexmap do
87
91
  "#{count} sitemap #{(count == 1) ? "file" : "files"}"
88
92
  end
89
93
 
90
- def public_directory(runner)
91
- runner.public_path
94
+ def storage_description(runner)
95
+ runner.storage
96
+ end
97
+
98
+ def link_count(count)
99
+ "#{count} #{(count == 1) ? "link" : "links"}"
92
100
  end
93
101
 
94
102
  def format_google_ping_failure(failure)
@@ -8,41 +8,49 @@ class IndexmapConfigurationTest < Minitest::Test
8
8
  end
9
9
 
10
10
  def test_writer_builds_from_configured_callables
11
- Dir.mktmpdir do |dir|
12
- public_path = Pathname(dir)
13
-
14
- Indexmap.configure do |config|
15
- config.base_url = -> { "https://example.com" }
16
- config.public_path = -> { public_path }
17
- config.sections = -> do
18
- [Indexmap::Section.new(filename: "sitemap-pages.xml", entries: [Indexmap::Entry.new(loc: "https://example.com/")])]
19
- end
11
+ Indexmap.configure do |config|
12
+ config.base_url = -> { "https://example.com" }
13
+ config.sections = -> do
14
+ [Indexmap::Section.new(filename: "sitemap-pages.xml", entries: [Indexmap::Entry.new(loc: "https://example.com/")])]
20
15
  end
16
+ end
17
+
18
+ files = Indexmap.configuration.writer.write
21
19
 
22
- Indexmap.configuration.writer.write
20
+ assert_includes files.find { |file| file.filename == "sitemap.xml" }.body, "<loc>https://example.com/sitemap-pages.xml</loc>"
21
+ assert_includes files.find { |file| file.filename == "sitemap-pages.xml" }.body, "<loc>https://example.com/</loc>"
22
+ end
23
+
24
+ def test_create_writes_to_configured_storage
25
+ storage = Indexmap::Storage::Memory.new
23
26
 
24
- assert_includes public_path.join("sitemap.xml").read, "<loc>https://example.com/sitemap-pages.xml</loc>"
25
- assert_includes public_path.join("sitemap-pages.xml").read, "<loc>https://example.com/</loc>"
27
+ Indexmap.configure do |config|
28
+ config.base_url = "https://example.com"
29
+ config.storage = storage
30
+ config.sections = [
31
+ Indexmap::Section.new(filename: "sitemap-pages.xml", entries: [Indexmap::Entry.new(loc: "https://example.com/")])
32
+ ]
26
33
  end
34
+
35
+ files = Indexmap.create
36
+
37
+ assert_equal ["sitemap-pages.xml", "sitemap.xml"], files
38
+ assert_includes storage.read("sitemap.xml"), "<loc>https://example.com/sitemap-pages.xml</loc>"
39
+ assert_includes storage.read("sitemap-pages.xml"), "<loc>https://example.com/</loc>"
27
40
  end
28
41
 
29
42
  def test_writer_builds_single_file_writer_from_configured_entries
30
- Dir.mktmpdir do |dir|
31
- public_path = Pathname(dir)
32
-
33
- Indexmap.configure do |config|
34
- config.base_url = "https://example.com"
35
- config.public_path = public_path
36
- config.format = :single_file
37
- config.entries = -> { [Indexmap::Entry.new(loc: "https://example.com/")] }
38
- end
43
+ Indexmap.configure do |config|
44
+ config.base_url = "https://example.com"
45
+ config.format = :single_file
46
+ config.entries = -> { [Indexmap::Entry.new(loc: "https://example.com/")] }
47
+ end
39
48
 
40
- Indexmap.configuration.writer.write
49
+ files = Indexmap.configuration.writer.write
41
50
 
42
- assert_includes public_path.join("sitemap.xml").read, "<urlset"
43
- assert_includes public_path.join("sitemap.xml").read, "<loc>https://example.com/</loc>"
44
- refute public_path.join("sitemap-pages.xml").exist?
45
- end
51
+ assert_equal ["sitemap.xml"], files.map(&:filename)
52
+ assert_includes files.fetch(0).body, "<urlset"
53
+ assert_includes files.fetch(0).body, "<loc>https://example.com/</loc>"
46
54
  end
47
55
 
48
56
  def test_writer_raises_without_base_url
@@ -83,141 +91,102 @@ class IndexmapConfigurationTest < Minitest::Test
83
91
  config.google.credentials = -> { "{\"type\":\"service_account\"}" }
84
92
  config.google.property = -> { "sc-domain:example.com" }
85
93
  config.index_now.key = -> { "example-key" }
94
+ config.index_now.key_filename = -> { "index-now-key.txt" }
86
95
  config.index_now.max_urls_per_request = -> { 250 }
96
+ config.index_now.write_key_file = -> { false }
87
97
  end
88
98
 
89
99
  assert_equal "{\"type\":\"service_account\"}", Indexmap.configuration.google.credentials
90
100
  assert_equal "sc-domain:example.com", Indexmap.configuration.google.property
91
101
  assert_equal "example-key", Indexmap.configuration.index_now.key
102
+ assert_equal "index-now-key.txt", Indexmap.configuration.index_now.key_filename
92
103
  assert_equal 250, Indexmap.configuration.index_now.max_urls_per_request
104
+ refute Indexmap.configuration.index_now.write_key_file?
93
105
  end
94
106
 
95
- def test_named_outputs_inherit_configuration_defaults
96
- Dir.mktmpdir do |dir|
97
- public_path = Pathname(dir)
98
-
99
- Indexmap.configure do |config|
100
- config.base_url = "https://example.com"
101
- config.public_path = public_path
102
- config.output :reports do |output|
103
- output.sections = [
104
- Indexmap::Section.new(
105
- filename: "sitemap-reports.xml",
106
- entries: [Indexmap::Entry.new(loc: "https://example.com/reports")]
107
- )
108
- ]
109
- end
110
- end
107
+ def test_index_now_key_file_writing_defaults_to_configured_key_presence
108
+ config = Indexmap::Configuration.new
111
109
 
112
- files = Indexmap.create(:reports)
110
+ refute config.index_now.write_key_file?
113
111
 
114
- assert_equal [
115
- public_path.join("sitemap-reports.xml"),
116
- public_path.join("sitemap.xml")
117
- ], files
118
- assert_includes public_path.join("sitemap.xml").read, "https://example.com/sitemap-reports.xml"
119
- end
120
- end
112
+ config.index_now.key = "1234567890abcdef1234567890abcdef"
121
113
 
122
- def test_create_writes_named_output_to_public_path
123
- Dir.mktmpdir do |dir|
124
- public_path = Pathname(dir)
125
-
126
- Indexmap.configure do |config|
127
- config.base_url = "https://example.com"
128
- config.public_path = public_path
129
- config.output :dynamic do |output|
130
- output.sections = [
131
- Indexmap::Section.new(
132
- filename: "sitemap-dynamic.xml",
133
- entries: [Indexmap::Entry.new(loc: "https://example.com/dynamic")]
134
- )
135
- ]
136
- end
137
- end
114
+ assert config.index_now.write_key_file?
115
+ end
138
116
 
139
- files = Indexmap.create(:dynamic)
117
+ def test_index_now_key_file_writing_can_be_disabled_with_a_configured_key
118
+ config = Indexmap::Configuration.new
119
+ config.index_now.key = "1234567890abcdef1234567890abcdef"
120
+ config.index_now.write_key_file = false
140
121
 
141
- assert_equal [
142
- public_path.join("sitemap-dynamic.xml"),
143
- public_path.join("sitemap.xml")
144
- ], files
145
- assert_includes public_path.join("sitemap-dynamic.xml").read, "https://example.com/dynamic"
146
- assert_includes public_path.join("sitemap.xml").read, "https://example.com/sitemap-dynamic.xml"
147
- end
122
+ refute config.index_now.write_key_file?
148
123
  end
149
124
 
150
- def test_create_preserves_existing_files_when_validation_fails
151
- Dir.mktmpdir do |dir|
152
- public_path = Pathname(dir)
153
- public_path.join("sitemap.xml").write("old index")
154
- public_path.join("sitemap-pages.xml").write("old child")
155
-
156
- Indexmap.configure do |config|
157
- config.base_url = "https://example.com"
158
- config.public_path = public_path
159
- config.sections = [
125
+ def test_named_outputs_inherit_configuration_defaults
126
+ storage = Indexmap::Storage::Memory.new
127
+
128
+ Indexmap.configure do |config|
129
+ config.base_url = "https://example.com"
130
+ config.storage = storage
131
+ config.output :reports do |output|
132
+ output.sections = [
160
133
  Indexmap::Section.new(
161
- filename: "sitemap-pages.xml",
162
- entries: [Indexmap::Entry.new(loc: "https://example.com/about?utm_source=test")]
134
+ filename: "sitemap-reports.xml",
135
+ entries: [Indexmap::Entry.new(loc: "https://example.com/reports")]
163
136
  )
164
137
  ]
165
138
  end
139
+ end
166
140
 
167
- error = assert_raises(Indexmap::ValidationError) { Indexmap.create }
141
+ files = Indexmap.create(:reports)
168
142
 
169
- assert_match "Parameterized sitemap URLs detected", error.message
170
- assert_equal "old index", public_path.join("sitemap.xml").read
171
- assert_equal "old child", public_path.join("sitemap-pages.xml").read
172
- end
143
+ assert_equal ["sitemap-reports.xml", "sitemap.xml"], files
144
+ assert_includes storage.read("sitemap.xml"), "https://example.com/sitemap-reports.xml"
173
145
  end
174
146
 
175
147
  def test_create_writes_single_file_named_output_without_default_index
176
- Dir.mktmpdir do |dir|
177
- public_path = Pathname(dir)
178
-
179
- Indexmap.configure do |config|
180
- config.base_url = "https://example.com"
181
- config.public_path = public_path
182
- config.output :dynamic do |output|
183
- output.format = :single_file
184
- output.index_filename = "sitemap-dynamic.xml"
185
- output.entries = [
186
- Indexmap::Entry.new(loc: "https://example.com/dynamic")
187
- ]
188
- end
148
+ storage = Indexmap::Storage::Memory.new
149
+
150
+ Indexmap.configure do |config|
151
+ config.base_url = "https://example.com"
152
+ config.storage = storage
153
+ config.output :dynamic do |output|
154
+ output.format = :single_file
155
+ output.index_filename = "sitemap-dynamic.xml"
156
+ output.entries = [
157
+ Indexmap::Entry.new(loc: "https://example.com/dynamic")
158
+ ]
189
159
  end
160
+ end
190
161
 
191
- files = Indexmap.create(:dynamic)
162
+ files = Indexmap.create(:dynamic)
192
163
 
193
- assert_equal [public_path.join("sitemap-dynamic.xml")], files
194
- refute public_path.join("sitemap.xml").exist?
195
- assert_includes public_path.join("sitemap-dynamic.xml").read, "https://example.com/dynamic"
196
- end
164
+ assert_equal ["sitemap-dynamic.xml"], files
165
+ refute storage.exist?("sitemap.xml")
166
+ assert_includes storage.read("sitemap-dynamic.xml"), "https://example.com/dynamic"
197
167
  end
198
168
 
199
- def test_create_preserves_existing_named_output_when_validation_fails
200
- Dir.mktmpdir do |dir|
201
- public_path = Pathname(dir)
202
- public_path.join("sitemap-dynamic.xml").write("old dynamic")
203
-
204
- Indexmap.configure do |config|
205
- config.base_url = "https://example.com"
206
- config.public_path = public_path
207
- config.output :dynamic do |output|
208
- output.format = :single_file
209
- output.index_filename = "sitemap-dynamic.xml"
210
- output.entries = [
211
- Indexmap::Entry.new(loc: "https://example.com/dynamic?utm_source=test")
212
- ]
213
- end
214
- end
215
-
216
- error = assert_raises(Indexmap::ValidationError) { Indexmap.create(:dynamic) }
169
+ def test_create_preserves_existing_files_when_validation_fails
170
+ storage = Indexmap::Storage::Memory.new
171
+ storage.write("sitemap.xml", "old index")
172
+ storage.write("sitemap-pages.xml", "old child")
217
173
 
218
- assert_match "Parameterized sitemap URLs detected", error.message
219
- assert_equal "old dynamic", public_path.join("sitemap-dynamic.xml").read
174
+ Indexmap.configure do |config|
175
+ config.base_url = "https://example.com"
176
+ config.storage = storage
177
+ config.sections = [
178
+ Indexmap::Section.new(
179
+ filename: "sitemap-pages.xml",
180
+ entries: [Indexmap::Entry.new(loc: "https://example.com/about?utm_source=test")]
181
+ )
182
+ ]
220
183
  end
184
+
185
+ error = assert_raises(Indexmap::ValidationError) { Indexmap.create }
186
+
187
+ assert_match "Parameterized sitemap URLs detected", error.message
188
+ assert_equal "old index", storage.read("sitemap.xml")
189
+ assert_equal "old child", storage.read("sitemap-pages.xml")
221
190
  end
222
191
 
223
192
  def test_after_create_requires_a_block
@@ -16,7 +16,7 @@ class IndexmapParserTest < Minitest::Test
16
16
  XML
17
17
  )
18
18
 
19
- parser = Indexmap::Parser.new(path: "https://www.example.com/sitemap.xml")
19
+ parser = Indexmap::Parser.new(source: "https://www.example.com/sitemap.xml")
20
20
 
21
21
  assert_equal ["/", "/pages/features"], parser.paths
22
22
  end
@@ -44,7 +44,7 @@ class IndexmapParserTest < Minitest::Test
44
44
  XML
45
45
  )
46
46
 
47
- parser = Indexmap::Parser.new(path: "https://www.example.com/sitemap.xml")
47
+ parser = Indexmap::Parser.new(source: "https://www.example.com/sitemap.xml")
48
48
 
49
49
  assert_equal ["/tools/google-reviews-calculator"], parser.paths
50
50
  assert_equal ["https://www.reviato.com/tools/google-reviews-calculator"], parser.urls(base_url: "https://www.reviato.com")
@@ -73,9 +73,50 @@ class IndexmapParserTest < Minitest::Test
73
73
  XML
74
74
  )
75
75
 
76
- parser = Indexmap::Parser.new(path: "http://localhost:3001/sitemap.xml", rebase_remote_children: true)
76
+ parser = Indexmap::Parser.new(source: "http://localhost:3001/sitemap.xml", rebase_remote_children: true)
77
77
 
78
78
  assert_equal ["/pages/pricing"], parser.paths
79
79
  assert_equal ["http://localhost:3001/pages/pricing"], parser.urls(base_url: "http://localhost:3001")
80
80
  end
81
+
82
+ def test_parses_storage_sitemap_index_with_directory_keys
83
+ storage = Indexmap::Storage::Memory.new
84
+ storage.write("sitemaps/sitemap.xml", <<~XML)
85
+ <?xml version="1.0" encoding="UTF-8"?>
86
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
87
+ <sitemap><loc>https://www.example.com/sitemaps/content.xml</loc></sitemap>
88
+ </sitemapindex>
89
+ XML
90
+ storage.write("sitemaps/content.xml", <<~XML)
91
+ <?xml version="1.0" encoding="UTF-8"?>
92
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
93
+ <url><loc>https://www.example.com/pages/features</loc></url>
94
+ </urlset>
95
+ XML
96
+
97
+ parser = Indexmap::Parser.new(source: "sitemaps/sitemap.xml", storage: storage)
98
+
99
+ assert_equal ["/pages/features"], parser.paths
100
+ assert_equal ["sitemaps/content.xml"], parser.entries.map(&:source_sitemap)
101
+ end
102
+
103
+ def test_parses_relative_child_sitemaps_from_parent_directory
104
+ storage = Indexmap::Storage::Memory.new
105
+ storage.write("sitemaps/sitemap.xml", <<~XML)
106
+ <?xml version="1.0" encoding="UTF-8"?>
107
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
108
+ <sitemap><loc>content.xml</loc></sitemap>
109
+ </sitemapindex>
110
+ XML
111
+ storage.write("sitemaps/content.xml", <<~XML)
112
+ <?xml version="1.0" encoding="UTF-8"?>
113
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
114
+ <url><loc>https://www.example.com/pages/pricing</loc></url>
115
+ </urlset>
116
+ XML
117
+
118
+ parser = Indexmap::Parser.new(source: "sitemaps/sitemap.xml", storage: storage)
119
+
120
+ assert_equal ["/pages/pricing"], parser.paths
121
+ end
81
122
  end