require 'sitemap_generator/builder/helper' require 'builder' require 'zlib' module SitemapGenerator module Builder class SitemapFile include SitemapGenerator::Builder::Helper attr_accessor :sitemap_path, :public_path, :filesize, :link_count, :hostname # public_path full path of the directory to write sitemaps in. # Usually your Rails public/ directory. # # sitemap_path relative path including filename of the sitemap # file relative to public_path # # hostname hostname including protocol to use in all links # e.g. http://en.google.ca def initialize(public_path, sitemap_path, hostname) self.sitemap_path = sitemap_path self.public_path = public_path self.hostname = hostname self.link_count = 0 @xml_content = '' # XML urlset content @xml_wrapper_start = <<-HTML HTML @xml_wrapper_start.gsub!(/\s+/, ' ').gsub!(/ *> */, '>').strip! @xml_wrapper_end = %q[] self.filesize = bytesize(@xml_wrapper_start) + bytesize(@xml_wrapper_end) end def lastmod File.mtime(self.full_path) rescue nil end def empty? self.link_count == 0 end def full_url URI.join(self.hostname, self.sitemap_path).to_s end def full_path @full_path ||= File.join(self.public_path, self.sitemap_path) end # Return a boolean indicating whether the sitemap file can fit another link # of bytes bytes in size. def file_can_fit?(bytes) (self.filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && self.link_count < SitemapGenerator::MAX_SITEMAP_LINKS end # Add a link to the sitemap file and return a boolean indicating whether the # link was added. # # If a link cannot be added, the file is too large or the link limit has been reached. def add_link(link) xml = build_xml(::Builder::XmlMarkup.new, link) unless file_can_fit?(bytesize(xml)) self.finalize! return false end @xml_content << xml self.filesize += bytesize(xml) self.link_count += 1 true end alias_method :<<, :add_link # Return XML as a String def build_xml(builder, link) builder.url do builder.loc link[:loc] builder.lastmod w3c_date(link[:lastmod]) if link[:lastmod] builder.changefreq link[:changefreq] if link[:changefreq] builder.priority link[:priority] if link[:priority] unless link[:images].blank? link[:images].each do |image| builder.image:image do builder.image :loc, image[:loc] builder.image :caption, image[:caption] if image[:caption] builder.image :geo_location, image[:geo_location] if image[:geo_location] builder.image :title, image[:title] if image[:title] builder.image :license, image[:license] if image[:license] end end end end builder << '' end # Insert the content into the XML "wrapper" and write and close the file. # # All the xml content in the instance is cleared, but attributes like # filesize are still available. def finalize! return if self.frozen? open(self.full_path, 'wb') do |file| gz = Zlib::GzipWriter.new(file) gz.write @xml_wrapper_start gz.write @xml_content gz.write @xml_wrapper_end gz.close end @xml_content = @xml_wrapper_start = @xml_wrapper_end = '' self.freeze end # Return the bytesize length of the string def bytesize(string) string.respond_to?(:bytesize) ? string.bytesize : string.length end end end end