sitemap_generator-cache_adapter 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: febec451f961b8b328763defdc44cb4b1cc9c2d294ef2f9979f479c04b5b0d40
4
+ data.tar.gz: 85f82fd0768b2f4e1d86c8304f50ab2b529c2aea9901bf52981f970102255eef
5
+ SHA512:
6
+ metadata.gz: 9bc7e95fad17474e16acb7975c1e5a1ed4d2298ac5af25609e05b677aea4476a2b8975b42b61d8a8faba8408d7e57f6df2fd681933a3757f97701b777fbc569c
7
+ data.tar.gz: 5290c796fc45b8a71a9a6828d5da2b6985fed371b903f43f8754f90db91121741f49098b5be3c166ac190e2388ed175ce9c8526c7e4df9985811091631bc842f
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
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
+
8
+ ## [1.0.0] - 2026-01-09
9
+
10
+ ### Added
11
+
12
+ - Initial release
13
+ - `SitemapGenerator::CacheAdapter` class for storing sitemaps in Rails.cache
14
+ - Support for custom cache key prefix and expiration time
15
+ - Support for custom cache store
16
+ - Class methods: `fetch`, `read`, `exist?`, `delete`, `clear_all`
17
+ - Comprehensive documentation and examples
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steve Hill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # SitemapGenerator::CacheAdapter
2
+
3
+ A cache-based storage adapter for the [sitemap_generator](https://github.com/kjvarga/sitemap_generator) gem. Stores sitemaps in `Rails.cache` instead of the filesystem.
4
+
5
+ ## Why?
6
+
7
+ The default `sitemap_generator` adapter writes sitemaps to disk. This doesn't work well for:
8
+
9
+ - **Kubernetes deployments** with ephemeral pod storage
10
+ - **Heroku** and other read-only filesystems
11
+ - **Multi-pod deployments** where sitemaps need to be shared across instances
12
+ - **Serverless environments** without persistent storage
13
+
14
+ This adapter stores sitemaps in your Rails cache instead. When used with a database-backed cache like [Solid Cache](https://github.com/rails/solid_cache), sitemaps persist across deploys and are shared across all application instances.
15
+
16
+ ## Installation
17
+
18
+ Add to your Gemfile:
19
+
20
+ ```ruby
21
+ gem "sitemap_generator-cache_adapter"
22
+ ```
23
+
24
+ Then run:
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### Basic Setup
33
+
34
+ Configure `sitemap_generator` to use the cache adapter in `config/sitemap.rb`:
35
+
36
+ ```ruby
37
+ SitemapGenerator::Sitemap.default_host = "https://example.com"
38
+ SitemapGenerator::Sitemap.adapter = SitemapGenerator::CacheAdapter.new
39
+ SitemapGenerator::Sitemap.compress = false # Recommended for cache storage
40
+ SitemapGenerator::Sitemap.create_index = false # Single sitemap file
41
+
42
+ SitemapGenerator::Sitemap.create do
43
+ add "/about", changefreq: "monthly"
44
+ add "/contact", changefreq: "monthly"
45
+
46
+ # Add dynamic content
47
+ Post.find_each do |post|
48
+ add post_path(post), lastmod: post.updated_at
49
+ end
50
+ end
51
+ ```
52
+
53
+ ### Serving Sitemaps
54
+
55
+ Create a controller to serve sitemaps from the cache:
56
+
57
+ ```ruby
58
+ # app/controllers/sitemaps_controller.rb
59
+ class SitemapsController < ApplicationController
60
+ def show
61
+ xml = SitemapGenerator::CacheAdapter.fetch("sitemap.xml") do
62
+ # This block runs on cache miss - generates and caches the sitemap
63
+ SitemapGenerator::Sitemap.create
64
+ end
65
+
66
+ if xml.present?
67
+ render xml: xml
68
+ else
69
+ head :not_found
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ Add the route:
76
+
77
+ ```ruby
78
+ # config/routes.rb
79
+ get "sitemap.xml", to: "sitemaps#show", defaults: { format: :xml }
80
+ ```
81
+
82
+ ### Configuration Options
83
+
84
+ ```ruby
85
+ SitemapGenerator::Sitemap.adapter = SitemapGenerator::CacheAdapter.new(
86
+ cache_key_prefix: "myapp:sitemap", # Default: "sitemap_generator"
87
+ expires_in: 12.hours, # Default: 24.hours
88
+ cache_store: Rails.cache # Default: Rails.cache
89
+ )
90
+ ```
91
+
92
+ ### Class Methods
93
+
94
+ The adapter provides several class methods for working with cached sitemaps:
95
+
96
+ ```ruby
97
+ # Fetch with lazy generation
98
+ xml = SitemapGenerator::CacheAdapter.fetch("sitemap.xml") do
99
+ SitemapGenerator::Sitemap.create
100
+ end
101
+
102
+ # Read directly (returns nil if not cached)
103
+ xml = SitemapGenerator::CacheAdapter.read("sitemap.xml")
104
+
105
+ # Check existence
106
+ if SitemapGenerator::CacheAdapter.exist?("sitemap.xml")
107
+ # ...
108
+ end
109
+
110
+ # Delete a specific sitemap
111
+ SitemapGenerator::CacheAdapter.delete("sitemap.xml")
112
+
113
+ # Clear all cached sitemaps
114
+ SitemapGenerator::CacheAdapter.clear_all
115
+ ```
116
+
117
+ ## How It Works
118
+
119
+ 1. **On cache miss**: The `fetch` method yields to the block, which calls `SitemapGenerator::Sitemap.create`. This triggers the adapter's `write` method, storing the sitemap XML in the cache.
120
+
121
+ 2. **On cache hit**: The cached XML is returned immediately without regeneration.
122
+
123
+ 3. **Expiration**: Sitemaps expire after 24 hours by default. On the next request after expiration, a fresh sitemap is generated.
124
+
125
+ This "lazy regeneration" approach means:
126
+ - No scheduled jobs required
127
+ - Sitemaps are always fresh (within the cache TTL)
128
+ - First request after expiration may be slightly slower
129
+
130
+ ## Cache Backend Compatibility
131
+
132
+ Works with any Rails cache backend:
133
+
134
+ - **Solid Cache** (recommended for Kubernetes) - Database-backed, shared across pods
135
+ - **Redis** - Fast, shared across pods
136
+ - **Memcached** - Fast, shared across pods
137
+ - **Memory Store** - Development/testing only (not shared)
138
+ - **File Store** - Single-server deployments only
139
+
140
+ ## Example: Kubernetes with Solid Cache
141
+
142
+ ```ruby
143
+ # Gemfile
144
+ gem "solid_cache"
145
+ gem "sitemap_generator"
146
+ gem "sitemap_generator-cache_adapter"
147
+
148
+ # config/environments/production.rb
149
+ config.cache_store = :solid_cache_store
150
+
151
+ # config/sitemap.rb
152
+ SitemapGenerator::Sitemap.adapter = SitemapGenerator::CacheAdapter.new
153
+ ```
154
+
155
+ With this setup, sitemaps are stored in your database and accessible from any pod in your cluster.
156
+
157
+ ## Migrating from File-Based Sitemaps
158
+
159
+ 1. Add the gem to your Gemfile
160
+ 2. Update `config/sitemap.rb` to use the cache adapter
161
+ 3. Create the sitemaps controller and route
162
+ 4. Remove any sitemap-related cron jobs or scheduled tasks
163
+ 5. Add `public/sitemap*.xml` to `.gitignore`
164
+ 6. Delete existing static sitemap files
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ bundle install
170
+ bundle exec rspec
171
+ bundle exec standardrb
172
+ ```
173
+
174
+ ## Contributing
175
+
176
+ Bug reports and pull requests are welcome on GitHub.
177
+
178
+ ## License
179
+
180
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SitemapGenerator
4
+ class CacheAdapter
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sitemap_generator"
4
+
5
+ module SitemapGenerator
6
+ # An adapter for SitemapGenerator that stores sitemaps in Rails.cache instead of the filesystem.
7
+ #
8
+ # This is useful for:
9
+ # - Kubernetes deployments with no persistent disk storage
10
+ # - Heroku and other read-only/ephemeral filesystems
11
+ # - Multi-pod deployments where sitemaps need to be shared across instances
12
+ #
13
+ # When used with a database-backed cache (like Solid Cache), sitemaps persist
14
+ # across deploys and are shared across all application instances.
15
+ #
16
+ # @example Basic usage in config/sitemap.rb
17
+ # SitemapGenerator::Sitemap.adapter = SitemapGenerator::CacheAdapter.new
18
+ #
19
+ # @example With custom options
20
+ # SitemapGenerator::Sitemap.adapter = SitemapGenerator::CacheAdapter.new(
21
+ # cache_key_prefix: "myapp:sitemap",
22
+ # expires_in: 12.hours
23
+ # )
24
+ #
25
+ # @example Serving from a Rails controller
26
+ # class SitemapsController < ApplicationController
27
+ # def show
28
+ # xml = SitemapGenerator::CacheAdapter.fetch("sitemap.xml") do
29
+ # SitemapGenerator::Sitemap.create
30
+ # end
31
+ # render xml: xml
32
+ # end
33
+ # end
34
+ #
35
+ class CacheAdapter
36
+ DEFAULT_CACHE_KEY_PREFIX = "sitemap_generator"
37
+ DEFAULT_EXPIRES_IN = 86_400 # 24 hours in seconds
38
+
39
+ class << self
40
+ # @return [String] The prefix used for all cache keys
41
+ def cache_key_prefix
42
+ @cache_key_prefix || DEFAULT_CACHE_KEY_PREFIX
43
+ end
44
+
45
+ # @param value [String] The prefix to use for all cache keys
46
+ attr_writer :cache_key_prefix
47
+
48
+ # @return [Integer, ActiveSupport::Duration] The cache expiration time
49
+ def expires_in
50
+ @expires_in || DEFAULT_EXPIRES_IN
51
+ end
52
+
53
+ # @param value [Integer, ActiveSupport::Duration] The cache expiration time
54
+ attr_writer :expires_in
55
+
56
+ # @return [#fetch, #read, #write, #exist?, #delete] The cache store to use
57
+ def cache_store
58
+ @cache_store || default_cache_store
59
+ end
60
+
61
+ # @param value [#fetch, #read, #write, #exist?, #delete] The cache store to use
62
+ attr_writer :cache_store
63
+
64
+ # Fetch a sitemap from cache, generating it if not present.
65
+ #
66
+ # @param filename [String] The sitemap filename (e.g., "sitemap.xml")
67
+ # @yield Block that generates the sitemap (typically calls SitemapGenerator::Sitemap.create)
68
+ # @return [String] The sitemap XML content
69
+ #
70
+ # @example
71
+ # xml = SitemapGenerator::CacheAdapter.fetch("sitemap.xml") do
72
+ # SitemapGenerator::Sitemap.create
73
+ # end
74
+ def fetch(filename, &block)
75
+ cache_key = "#{cache_key_prefix}:#{filename}"
76
+
77
+ cache_store.fetch(cache_key, expires_in: expires_in) do
78
+ yield if block_given?
79
+ cache_store.read(cache_key)
80
+ end
81
+ end
82
+
83
+ # Read a sitemap directly from cache without generation.
84
+ #
85
+ # @param filename [String] The sitemap filename
86
+ # @return [String, nil] The sitemap XML content or nil if not cached
87
+ def read(filename)
88
+ cache_store.read("#{cache_key_prefix}:#{filename}")
89
+ end
90
+
91
+ # Check if a sitemap exists in cache.
92
+ #
93
+ # @param filename [String] The sitemap filename
94
+ # @return [Boolean]
95
+ def exist?(filename)
96
+ cache_store.exist?("#{cache_key_prefix}:#{filename}")
97
+ end
98
+
99
+ # Delete a specific sitemap from cache.
100
+ #
101
+ # @param filename [String] The sitemap filename
102
+ # @return [Boolean] true if deleted, false otherwise
103
+ def delete(filename)
104
+ cache_store.delete("#{cache_key_prefix}:#{filename}")
105
+ end
106
+
107
+ # Clear all cached sitemaps matching the prefix.
108
+ #
109
+ # @note This only works with cache stores that support delete_matched
110
+ # @return [void]
111
+ def clear_all
112
+ if cache_store.respond_to?(:delete_matched)
113
+ cache_store.delete_matched("#{cache_key_prefix}:*")
114
+ else
115
+ # Fallback: delete known sitemap filenames
116
+ %w[sitemap.xml sitemap.xml.gz sitemap_index.xml sitemap_index.xml.gz].each do |f|
117
+ delete(f)
118
+ end
119
+ end
120
+ end
121
+
122
+ # Reset configuration to defaults.
123
+ #
124
+ # @return [void]
125
+ def reset!
126
+ @cache_key_prefix = nil
127
+ @expires_in = nil
128
+ @cache_store = nil
129
+ end
130
+
131
+ private
132
+
133
+ def default_cache_store
134
+ if defined?(Rails) && Rails.respond_to?(:cache)
135
+ Rails.cache
136
+ else
137
+ raise ConfigurationError, "No cache store configured. Set SitemapGenerator::CacheAdapter.cache_store or use within a Rails application."
138
+ end
139
+ end
140
+ end
141
+
142
+ # Error raised when the adapter is misconfigured
143
+ class ConfigurationError < StandardError; end
144
+
145
+ # Initialize a new CacheAdapter.
146
+ #
147
+ # @param cache_key_prefix [String] Prefix for cache keys (default: "sitemap_generator")
148
+ # @param expires_in [Integer, ActiveSupport::Duration] Cache expiration time (default: 24.hours)
149
+ # @param cache_store [#fetch, #read, #write] Cache store to use (default: Rails.cache)
150
+ #
151
+ # @example
152
+ # adapter = SitemapGenerator::CacheAdapter.new(
153
+ # cache_key_prefix: "myapp:sitemap",
154
+ # expires_in: 12.hours
155
+ # )
156
+ def initialize(cache_key_prefix: nil, expires_in: nil, cache_store: nil)
157
+ self.class.cache_key_prefix = cache_key_prefix if cache_key_prefix
158
+ self.class.expires_in = expires_in if expires_in
159
+ self.class.cache_store = cache_store if cache_store
160
+ end
161
+
162
+ # Write sitemap data to the cache.
163
+ #
164
+ # This method is called by SitemapGenerator when creating sitemaps.
165
+ # It conforms to the adapter interface expected by sitemap_generator.
166
+ #
167
+ # @param location [SitemapGenerator::SitemapLocation] Location object with filename info
168
+ # @param raw_data [String] The raw XML sitemap data
169
+ # @return [void]
170
+ def write(location, raw_data)
171
+ cache_key = "#{self.class.cache_key_prefix}:#{location.filename}"
172
+
173
+ self.class.cache_store.write(
174
+ cache_key,
175
+ raw_data,
176
+ expires_in: self.class.expires_in
177
+ )
178
+
179
+ log_write(location.filename, raw_data.bytesize)
180
+ end
181
+
182
+ private
183
+
184
+ def log_write(filename, bytesize)
185
+ if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
186
+ Rails.logger.info "[SitemapGenerator::CacheAdapter] Cached #{filename} (#{bytesize} bytes)"
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sitemap_generator/cache_adapter"
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sitemap_generator-cache_adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Steve Hill
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sitemap_generator
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: standard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: |
70
+ A cache-based storage adapter for the sitemap_generator gem. Stores sitemaps
71
+ in Rails.cache instead of the filesystem, making it ideal for Kubernetes
72
+ deployments, Heroku, and other environments with ephemeral or read-only
73
+ filesystems. Works with any Rails cache backend including Solid Cache,
74
+ Redis, and Memcached.
75
+ email:
76
+ - steve@stevehill.xyz
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - CHANGELOG.md
82
+ - LICENSE
83
+ - README.md
84
+ - lib/sitemap_generator-cache_adapter.rb
85
+ - lib/sitemap_generator/cache_adapter.rb
86
+ - lib/sitemap_generator/cache_adapter/version.rb
87
+ homepage: https://github.com/stevehill1981/sitemap_generator-cache_adapter
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/stevehill1981/sitemap_generator-cache_adapter
92
+ source_code_uri: https://github.com/stevehill1981/sitemap_generator-cache_adapter
93
+ changelog_uri: https://github.com/stevehill1981/sitemap_generator-cache_adapter/blob/main/CHANGELOG.md
94
+ rubygems_mfa_required: 'true'
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.1.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.5.22
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Cache adapter for sitemap_generator gem
114
+ test_files: []