module SitemapGenerator
module Builder
class SitemapIndexFile < SitemapFile
# === Options
#
# * location - a SitemapGenerator::SitemapIndexLocation instance or a Hash of options
# from which a SitemapLocation will be created for you.
def initialize(opts={})
@location = opts.is_a?(Hash) ? SitemapGenerator::SitemapIndexLocation.new(opts) : opts
@link_count = 0
@sitemaps_link_count = 0
@xml_content = '' # XML urlset content
@xml_wrapper_start = <<-HTML
HTML
@xml_wrapper_start.gsub!(/\s+/, ' ').gsub!(/ *> */, '>').strip!
@xml_wrapper_end = %q[]
@filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) + SitemapGenerator::Utilities.bytesize(@xml_wrapper_end)
@written = false
@reserved_name = nil # holds the name reserved from the namer
@frozen = false # rather than actually freeze, use this boolean
@first_sitemap = nil # reference to the first thing added to this index
# Store the URL of the first sitemap added because if create_index is
# false this is the "index" URL
@first_sitemap_url = nil
@create_index = nil
end
# Finalize sitemaps as they are added to the index.
# If it's the first sitemap, finalize it but don't
# write it out, because we don't yet know if we need an index. If it's
# the second sitemap, we know we need an index, so reserve a name for the
# index, and go and write out the first sitemap. If it's the third or
# greater sitemap, just finalize and write it out as usual, nothing more
# needs to be done.
#
# If a link is being added to the index manually as a string, then we
# can assume that the index is required (unless create_index is false of course).
# This seems like the logical thing to do.
alias_method :super_add, :add
def add(link, options={})
if file = link.is_a?(SitemapFile) && link
@sitemaps_link_count += file.link_count
file.finalize! unless file.finalized?
# First link. If it's a SitemapFile store a reference to it and the options
# so that we can create a URL from it later. We can't create the URL yet
# because doing so fixes the sitemap file's name, and we have to wait to see
# if we have more than one link in the index before we can know who gets the
# first name (the index, or the sitemap). If the item is not a SitemapFile,
# then it has been manually added and we can be sure that the user intends
# for there to be an index.
if @link_count == 0
@first_sitemap = SitemapGenerator::Builder::LinkHolder.new(file, options)
@link_count += 1 # pretend it's added, but don't add it yet
else
# need an index so make sure name is reserved and first sitemap is written out
reserve_name unless @location.create_index == false
write_first_sitemap
file.write
super(SitemapGenerator::Builder::SitemapIndexUrl.new(file, options))
end
else
# A link is being added manually. Obviously the user wants an index.
# This overrides the create_index setting.
unless @location.create_index == false
@create_index = true
reserve_name
end
# Use the host from the location if none provided
options[:host] ||= @location.host
super(SitemapGenerator::Builder::SitemapIndexUrl.new(link, options))
end
end
# Return a boolean indicating whether the sitemap file can fit another link
# of bytes bytes in size. You can also pass a string and the
# bytesize will be calculated for you.
def file_can_fit?(bytes)
bytes = bytes.is_a?(String) ? SitemapGenerator::Utilities.bytesize(bytes) : bytes
(@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && @link_count < SitemapGenerator::MAX_SITEMAP_FILES
end
# Return the total number of links in all sitemaps reference by this index file
def total_link_count
@sitemaps_link_count
end
def stats_summary(opts={})
str = "Sitemap stats: #{number_with_delimiter(@sitemaps_link_count)} links / #{@link_count} sitemaps"
str += " / %dm%02ds" % opts[:time_taken].divmod(60) if opts[:time_taken]
end
def finalize!
raise SitemapGenerator::SitemapFinalizedError if finalized?
reserve_name if create_index?
write_first_sitemap
@frozen = true
end
# Write out the index if an index is needed
def write
super if create_index?
end
# Whether or not we need to create an index file. True if create_index is true
# or if create_index is :auto and we have more than one link in the index.
# If a link is added manually and create_index is not false, we force index
# creation because they obviously intend for there to be an index. False otherwise.
def create_index?
@create_index || @location.create_index == true || @location.create_index == :auto && @link_count > 1
end
# Return the index file URL. If create_index is true, this is the URL
# of the actual index file. If create_index is false, this is the URL
# of the first sitemap that was written out. Only call this method
# *after* the files have been finalized.
def index_url
if create_index? || !@first_sitemap_url
@location.url
else
@first_sitemap_url
end
end
protected
# Make sure the first sitemap has been written out and added to the index
def write_first_sitemap
if @first_sitemap
@first_sitemap.link.write unless @first_sitemap.link.written?
super_add(SitemapGenerator::Builder::SitemapIndexUrl.new(@first_sitemap.link, @first_sitemap.options))
@link_count -= 1 # we already counted it, don't count it twice
# Store the URL because if create_index is false, this is the
# "index" URL
@first_sitemap_url = @first_sitemap.link.location.url
@first_sitemap = nil
end
end
end
end
end