How to add a dynamically generated sitemap with rails
The sitemap.xml
file on my blog is generated dynamically because I want it to include my latest posts in it.
The way I did that is that in a controller (in my case the HomeController
) I added an action which initializes the @ordered_ids
instance variable:
class HomeController < ApplicationController
def sitemap
@ordered_ids = Post.published.order(published_at: :desc).pluck(:id)
end
end
The config/routes.rb
file defines route route and explicitly sets the format:
get 'sitemap.xml', to: 'home#sitemap', format: 'xml', as: :sitemap
The app/views/home/sitemap.xml.builder
file then defines one static route and also defines routes depending on database entries (posts). These routes simply render the urls of static pages and dynamic ones (posts). Note how you can use the .xmlschema
method on date objects which returns the correct format for sitemaps.
I used the .find_each
method previously because I wanted to avoid loading all posts into memory at once but I noticed that you could not .order
the results by published_at
with .find_each
.
So the new solution grabs all ids in order in the action and then grabs them by groups of 1000, loads them into memory, defines the sitemap entries and then moves on to the next 1000 as described in this stackoverflow answer.
xml.instruct! :xml, :version=>"1.0"
xml.tag! 'urlset', 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9', 'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1', 'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1' do
xml.url do
xml.loc root_url
end
@ordered_ids.in_groups_of(1000, false).each do |ids|
posts = Post.order(published_at: :desc).where(id: ids)
posts.each do |post|
xml.url do
xml.loc show_post_url(post.slug)
xml.lastmod post.updated_at.xmlschema
end
end
end
end
Make sure your robots.txt
file then points to that sitemap like this for search engines to find that file correctly.
Sitemap: https://webdevchallenges.com/sitemap.xml