require 'sitemap_generator/helpers/number_helper' module SitemapGenerator # A class for determining the exact location at which to write sitemap data. # Handles reserving filenames from namers, constructing paths and sending # data to the adapter to be written out. class SitemapLocation < Hash include SitemapGenerator::Helpers::NumberHelper PATH_OUTPUT_WIDTH = 47 # Character width of the path in the summary lines [:host, :adapter].each do |method| define_method(method) do raise SitemapGenerator::SitemapError, "No value set for #{method}" unless self[method] self[method] end end [:public_path, :sitemaps_path].each do |method| define_method(method) do Pathname.new(SitemapGenerator::Utilities.append_slash(self[method])) end end # If no +filename+ or +namer+ is provided, the default namer is used, which # generates names like sitemap.xml.gz, sitemap1.xml.gz, sitemap2.xml.gz and so on. # # === Options # * :adapter - SitemapGenerator::Adapter subclass # * :filename - full name of the file e.g. 'sitemap1.xml.gz' # * :host - host name for URLs. The full URL to the file is then constructed from # the host, sitemaps_path and filename # * :namer - a SitemapGenerator::SimpleNamer instance for generating file names. # Should be passed if no +filename+ is provided. # * :public_path - path to the "public" directory, or the directory you want to # write sitemaps in. Default is a directory public/ # in the current working directory, or relative to the Rails root # directory if running under Rails. # * :sitemaps_path - gives the path relative to the public_path in which to # write sitemaps e.g. sitemaps/. # * :verbose - whether to output summary into to STDOUT. Default +false+. # * :create_index - whether to create a sitemap index. Default `:auto`. See LinkSet::create_index= # for possible values. Only applies to the SitemapIndexLocation object. # * compress - The LinkSet compress setting. Default: true. If `false` any `.gz` extension is # stripped from the filename. If `:all_but_first`, only the `.gz` extension of the first # filename is stripped off. If `true` the extensions are left unchanged. def initialize(opts={}) SitemapGenerator::Utilities.assert_valid_keys(opts, [:adapter, :public_path, :sitemaps_path, :host, :filename, :namer, :verbose, :create_index, :compress]) opts[:adapter] ||= SitemapGenerator::FileAdapter.new opts[:public_path] ||= SitemapGenerator.app.root + 'public/' # This is a bit of a hack to make the SimpleNamer act like the old SitemapNamer. # It doesn't really make sense to create a default namer like this because the # namer instance should be shared by the location objects of the sitemaps and # sitemap index files. However, this greatly eases testing, so I'm leaving it in # for now. if !opts[:filename] && !opts[:namer] opts[:namer] = SitemapGenerator::SimpleNamer.new(:sitemap, :start => 2, :zero => 1) end opts[:verbose] = !!opts[:verbose] self.merge!(opts) end # Return a new Location instance with the given options merged in def with(opts={}) self.merge(opts) end # Full path to the directory of the file. def directory (public_path + sitemaps_path).expand_path.to_s end # Full path of the file including the filename. def path (public_path + sitemaps_path + filename).expand_path.to_s end # Relative path of the file (including the filename) relative to public_path def path_in_public (sitemaps_path + filename).to_s end # Full URL of the file. def url URI.join(host, sitemaps_path.to_s, filename.to_s).to_s end # Return the size of the file at path def filesize File.size?(path) end # Return the filename. Raises an exception if no filename or namer is set. # If using a namer once the filename has been retrieved from the namer its # value is locked so that it is unaffected by further changes to the namer. def filename raise SitemapGenerator::SitemapError, "No filename or namer set" unless self[:filename] || self[:namer] unless self[:filename] self.send(:[]=, :filename, self[:namer].to_s, :super => true) # Post-process the filename for our compression settings. # Strip the `.gz` from the extension if we aren't compressing this file. # If you're setting the filename manually, :all_but_first won't work as # expected. Ultimately I should force using a namer in all circumstances. # Changing the filename here will affect how the FileAdapter writes out the file. if self[:compress] == false || (self[:namer] && self[:namer].start? && self[:compress] == :all_but_first) self[:filename].gsub!(/\.gz$/, '') end end self[:filename] end # If a namer is set, reserve the filename and increment the namer. # Returns the reserved name. def reserve_name if self[:namer] filename self[:namer].next end self[:filename] end # Return true if this location has a fixed filename. If no name has been # reserved from the namer, for instance, returns false. def reserved_name? !!self[:filename] end def namer self[:namer] end def verbose? self[:verbose] end # If you set the filename, clear the namer and vice versa. def []=(key, value, opts={}) if !opts[:super] case key when :namer super(:filename, nil) when :filename super(:namer, nil) end end super(key, value) end # Write `data` out to a file. # Output a summary line if verbose is true. def write(data, link_count) adapter.write(self, data) puts summary(link_count) if verbose? end # Return a summary string def summary(link_count) filesize = number_to_human_size(self.filesize) width = self.class::PATH_OUTPUT_WIDTH path = SitemapGenerator::Utilities.ellipsis(self.path_in_public, width) "+ #{('%-'+width.to_s+'s') % path} #{'%10s' % link_count} links / #{'%10s' % filesize}" end end class SitemapIndexLocation < SitemapLocation def initialize(opts={}) if !opts[:filename] && !opts[:namer] opts[:namer] = SitemapGenerator::SimpleNamer.new(:sitemap) end super(opts) end # Whether to create a sitemap index. Default `:auto`. See LinkSet::create_index= # for possible values. # # A placeholder for an option which should really go into some # kind of options class. def create_index self[:create_index] end # Return a summary string def summary(link_count) filesize = number_to_human_size(self.filesize) width = self.class::PATH_OUTPUT_WIDTH - 3 path = SitemapGenerator::Utilities.ellipsis(self.path_in_public, width) "+ #{('%-'+width.to_s+'s') % path} #{'%10s' % link_count} sitemaps / #{'%10s' % filesize}" end end end