sitemap_generator 1.5.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.2
1
+ 2.0.0
@@ -1,5 +1,5 @@
1
- require 'sitemap_generator/builder'
2
1
  require 'sitemap_generator/sitemap_namer'
2
+ require 'sitemap_generator/builder'
3
3
  require 'sitemap_generator/link_set'
4
4
  require 'sitemap_generator/templates'
5
5
  require 'sitemap_generator/utilities'
@@ -8,6 +8,8 @@ require 'sitemap_generator/sitemap_location'
8
8
  require 'active_support/core_ext/numeric'
9
9
 
10
10
  module SitemapGenerator
11
+ autoload(:Interpreter, 'sitemap_generator/interpreter')
12
+
11
13
  SitemapError = Class.new(StandardError)
12
14
  SitemapFullError = Class.new(SitemapError)
13
15
  SitemapFinalizedError = Class.new(SitemapError)
@@ -22,13 +24,24 @@ module SitemapGenerator
22
24
  # Lazy-initialize the LinkSet instance
23
25
  Sitemap = (Class.new do
24
26
  def method_missing(*args, &block)
25
- (@link_set ||= LinkSet.new).send(*args, &block)
27
+ (@link_set ||= reset!).send(*args, &block)
28
+ end
29
+
30
+ # Use a new LinkSet instance
31
+ def reset!
32
+ @link_set = LinkSet.new
26
33
  end
27
34
  end).new
28
35
  end
29
36
 
30
37
  class << self
31
38
  attr_accessor :root, :app, :templates
39
+ attr_writer :yield_sitemap
40
+ end
41
+
42
+ # Returns true if we should yield the sitemap instance to the block, false otherwise.
43
+ def self.yield_sitemap?
44
+ !!@yeild_sitemap
32
45
  end
33
46
 
34
47
  self.root = File.expand_path(File.join(File.dirname(__FILE__), '../'))
@@ -14,21 +14,14 @@ module SitemapGenerator
14
14
  class SitemapFile
15
15
  include ActionView::Helpers::NumberHelper
16
16
  include ActionView::Helpers::TextHelper # Rails 2.2.2 fails with missing 'pluralize' otherwise
17
- attr_reader :link_count, :filesize, :filename, :location
17
+ attr_reader :link_count, :filesize, :location
18
18
 
19
- # Options:
19
+ # === Options
20
20
  #
21
- # <tt>location</tt> a SitemapGenerator::SitemapLocation instance
22
- #
23
- # <tt>filename</tt> a symbol giving the base of the sitemap fileaname. Default: :sitemap
24
- #
25
- # <tt>namer</tt> (optional) if provided is used to get the next sitemap filename, overriding :filename
21
+ # * <tt>location</tt> - a SitemapGenerator::SitemapLocation instance or a Hash of options
22
+ # from which a SitemapLocation will be created for you.
26
23
  def initialize(opts={})
27
- SitemapGenerator::Utilities.assert_valid_keys(opts, [:location, :filename, :namer])
28
-
29
- @location = opts.delete(:location) || SitemapGenerator::SitemapLocation.new
30
- @namer = opts.delete(:namer) || new_namer(opts.delete(:filename))
31
- @filename = @location[:filename] = @namer.next
24
+ @location = opts.is_a?(Hash) ? SitemapGenerator::SitemapLocation.new(opts) : opts
32
25
  @link_count = 0
33
26
  @xml_content = '' # XML urlset content
34
27
  @xml_wrapper_start = <<-HTML
@@ -102,20 +95,25 @@ module SitemapGenerator
102
95
 
103
96
  # Ensure that the directory exists
104
97
  dir = @location.directory
105
- path = @location.path
106
98
  if !File.exists?(dir)
107
99
  FileUtils.mkdir_p(dir)
108
100
  elsif !File.directory?(dir)
109
101
  raise SitemapError.new("#{dir} should be a directory!")
110
102
  end
111
103
 
112
- open(path, 'wb') do |file|
104
+ # Write out the file
105
+ open(@location.path, 'wb') do |file|
113
106
  gz = Zlib::GzipWriter.new(file)
114
107
  gz.write @xml_wrapper_start
115
108
  gz.write @xml_content
116
109
  gz.write @xml_wrapper_end
117
110
  gz.close
118
111
  end
112
+
113
+ # Increment the namer (SitemapFile only)
114
+ @location.namer.next if @location.namer
115
+
116
+ # Cleanup and freeze the object
119
117
  @xml_content = @xml_wrapper_start = @xml_wrapper_end = ''
120
118
  freeze
121
119
  end
@@ -125,8 +123,10 @@ module SitemapGenerator
125
123
  end
126
124
 
127
125
  # Return a new instance of the sitemap file with the same options, and the next name in the sequence.
128
- def next
129
- self.class.new(:location => @location.dup, :namer => @namer)
126
+ def new
127
+ location = @location.dup
128
+ location.delete(:filename) if location.namer
129
+ self.class.new(location)
130
130
  end
131
131
 
132
132
  # Return a summary string
@@ -136,26 +136,12 @@ module SitemapGenerator
136
136
  "+ #{'%-21s' % @location.path_in_public} #{'%13s' % @link_count} links / #{'%10s' % uncompressed_size} / #{'%10s' % compressed_size} gzipped"
137
137
  end
138
138
 
139
- # Create a new namer given a filename base and set the filename of this sitemap from it.
140
- # It is a bit confusing because the setter takes a filename base whereas the getter
141
- # returns a full filename including extension.
142
- def filename=(base)
143
- @namer = new_namer(base)
144
- @filename = @location[:filename] = @namer.next
145
- end
146
-
147
139
  protected
148
140
 
149
- # Return a new namer given a filename base and set the filename of this sitemap from it.
150
- # Default filename base is 'sitemap'.
151
- def new_namer(base=nil)
152
- SitemapGenerator::SitemapNamer.new(base ||= :sitemap)
153
- end
154
-
155
141
  # Return the bytesize length of the string. Ruby 1.8.6 compatible.
156
142
  def bytesize(string)
157
143
  string.respond_to?(:bytesize) ? string.bytesize : string.length
158
144
  end
159
145
  end
160
146
  end
161
- end
147
+ end
@@ -1,16 +1,13 @@
1
1
  module SitemapGenerator
2
2
  module Builder
3
3
  class SitemapIndexFile < SitemapFile
4
- attr_accessor :sitemaps
5
4
 
5
+ # === Options
6
+ #
7
+ # * <tt>location</tt> - a SitemapGenerator::SitemapIndexLocation instance or a Hash of options
8
+ # from which a SitemapLocation will be created for you.
6
9
  def initialize(opts={})
7
- @options = [:location, :filename]
8
- SitemapGenerator::Utilities.assert_valid_keys(opts, @options)
9
-
10
- @location = opts.delete(:location) || SitemapGenerator::SitemapLocation.new
11
- @filename = "#{opts.fetch(:filename, :sitemap_index)}.xml.gz"
12
- @location[:filename] = @filename
13
-
10
+ @location = opts.is_a?(Hash) ? SitemapGenerator::SitemapIndexLocation.new(opts) : opts
14
11
  @link_count = 0
15
12
  @sitemaps_link_count = 0
16
13
  @xml_content = '' # XML urlset content
@@ -37,16 +34,19 @@ module SitemapGenerator
37
34
  super(SitemapGenerator::Builder::SitemapIndexUrl.new(link, options))
38
35
  end
39
36
 
37
+ # Return a boolean indicating whether the sitemap file can fit another link
38
+ # of <tt>bytes</tt> bytes in size. You can also pass a string and the
39
+ # bytesize will be calculated for you.
40
+ def file_can_fit?(bytes)
41
+ bytes = bytes.is_a?(String) ? bytesize(bytes) : bytes
42
+ (@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && @link_count < SitemapGenerator::MAX_SITEMAP_FILES
43
+ end
44
+
40
45
  # Return the total number of links in all sitemaps reference by this index file
41
46
  def total_link_count
42
47
  @sitemaps_link_count
43
48
  end
44
49
 
45
- # Set a new filename on the instance. Should not include any extensions e.g. :sitemap_index
46
- def filename=(filename)
47
- @filename = @location[:filename] = "#{filename}_index.xml.gz"
48
- end
49
-
50
50
  # Return a summary string
51
51
  def summary(opts={})
52
52
  uncompressed_size = number_to_human_size(@filesize) rescue "#{@filesize / 8} KB"
@@ -60,4 +60,4 @@ module SitemapGenerator
60
60
  end
61
61
  end
62
62
  end
63
- end
63
+ end
@@ -2,8 +2,8 @@ require 'sitemap_generator'
2
2
 
3
3
  module SitemapGenerator
4
4
 
5
- # Evaluate a sitemap config file within the context of a class that includes the
6
- # Rails URL helpers.
5
+ # Provide a class for evaluating blocks, making the URL helpers from the framework
6
+ # and API methods available to it.
7
7
  class Interpreter
8
8
 
9
9
  if SitemapGenerator.app.rails3?
@@ -16,26 +16,54 @@ module SitemapGenerator
16
16
  # Call with a block to evaluate a dynamic config. The only method exposed for you is
17
17
  # `add` to add a link to the sitemap object attached to this interpreter.
18
18
  #
19
- # @param sitemap a sitemap object
20
- # @param sitemap_config_file full path to the config file (default is config/sitemap.rb)
21
- def initialize(sitemap, sitemap_config_file=nil, &block)
22
- @sitemap = sitemap
23
- if block_given?
24
- instance_eval(&block)
25
- else
26
- sitemap_config_file ||= SitemapGenerator.app.root + 'config/sitemap.rb'
27
- eval(File.read(sitemap_config_file), nil, sitemap_config_file.to_s)
28
- end
19
+ # === Options
20
+ # * <tt>link_set</tt> - a LinkSet instance to use. Default is SitemapGenerator::Sitemap.
21
+ #
22
+ # All other options are passed to the LinkSet by setting them using accessor methods.
23
+ def initialize(opts={}, &block)
24
+ opts.reverse_merge!(:link_set => SitemapGenerator::Sitemap)
25
+ @linkset = opts.delete :link_set
26
+ @linkset.send(:set_options, opts)
27
+ eval(&block) if block_given?
29
28
  end
30
29
 
31
30
  def add(*args)
32
- @sitemap.add(*args)
31
+ @linkset.add(*args)
32
+ end
33
+
34
+ # Start a new group of sitemaps. Any of the options to SitemapGenerator.new may
35
+ # be passed. Pass a block with calls to +add+ to add links to the sitemaps.
36
+ #
37
+ # All groups use the same sitemap index.
38
+ def group(*args, &block)
39
+ @linkset.group(*args, &block)
33
40
  end
34
41
 
35
- # Evaluate the sitemap config file in this namespace which includes the
36
- # URL helpers.
37
- def self.run
38
- new(SitemapGenerator::Sitemap)
42
+ # Evaluate the block in the interpreter. Pass :yield_sitemap => true to
43
+ # yield the Interpreter instance to the block...for old-style calling.
44
+ def eval(opts={}, &block)
45
+ if block_given?
46
+ if opts[:yield_sitemap]
47
+ yield self
48
+ else
49
+ instance_eval(&block)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Run the interpreter on a config file using
55
+ # the default <tt>SitemapGenerator::Sitemap</tt> sitemap object.
56
+ #
57
+ # === Options
58
+ # * <tt>:config_file</tt> - full path to the config file to evaluate.
59
+ # Default is config/sitemap.rb in your application's root directory.
60
+ # All other options are passed to +new+.
61
+ def self.run(opts={}, &block)
62
+ config_file = opts.delete(:config_file)
63
+ config_file ||= SitemapGenerator.app.root + 'config/sitemap.rb'
64
+ interpreter = self.new(opts)
65
+ interpreter.instance_eval(File.read(config_file), config_file.to_s)
66
+ interpreter
39
67
  end
40
68
  end
41
- end
69
+ end
@@ -4,100 +4,90 @@ require 'builder'
4
4
  # which lists all the sitemap files written.
5
5
  module SitemapGenerator
6
6
  class LinkSet
7
+ @@requires_finalization_opts = [:filename, :sitemaps_path, :sitemaps_namer, :sitemaps_host]
8
+ @@new_location_opts = [:filename, :sitemaps_path, :sitemaps_namer]
7
9
 
8
- attr_reader :default_host, :public_path, :sitemaps_path, :filename, :sitemap, :location
9
- attr_accessor :verbose, :yahoo_app_id, :include_root, :include_index
10
+ attr_reader :default_host, :sitemaps_path, :filename
11
+ attr_accessor :verbose, :yahoo_app_id, :include_root, :include_index, :sitemaps_host
10
12
 
11
- # Evaluate the sitemap config file and write all sitemaps.
13
+ # Add links to the link set by evaluating the block. The block should
14
+ # contains calls to sitemap methods like:
15
+ # * +add+ - Add a link to the current sitemap
16
+ # * +group+ - Start a new group of sitemaps
12
17
  #
13
- # The Sitemap Interpreter includes the URL helpers and API methods
14
- # that the block argument to `add_links` is evaluted within.
18
+ # == Options
15
19
  #
16
- # TODO: Refactor so that we can have multiple instances
17
- # of LinkSet.
18
- def create(config_file = 'config/sitemap.rb', &block)
19
- require 'sitemap_generator/interpreter'
20
-
21
- # Clear out the current objects. New objects will be lazy-initialized.
22
- @sitemap_index = @sitemap = nil
23
-
24
- start_time = Time.now
25
- SitemapGenerator::Interpreter.new(self, config_file, &block)
26
- finalize_sitemap
27
- finalize_sitemap_index
28
- end_time = Time.now
20
+ # Any option supported by +new+ can be passed. The options will be
21
+ # set on the instance using the accessor methods. This is provided mostly
22
+ # as a convenience.
23
+ #
24
+ # In addition to the options to +new+, the following options are supported:
25
+ # * <tt>:finalize</tt> - The sitemaps are written as they get full and at the end
26
+ # of the block. Pass +false+ as the value to prevent the sitemap or sitemap index
27
+ # from being finalized. Default is +true+.
28
+ def create(opts={}, &block)
29
+ @sitemap_index = nil if @sitemap_index && @sitemap_index.finalized? && !@protect_index
30
+ @sitemap = nil if @sitemap && @sitemap.finalized?
31
+ set_options(opts)
32
+ start_time = Time.now if @verbose
33
+ interpreter.eval(:yield_sitemap => @yield_sitemap || SitemapGenerator.yield_sitemap?, &block)
34
+ finalize!
35
+ end_time = Time.now if @verbose
36
+ puts sitemap_index.stats_summary(:time_taken => end_time - start_time) if @verbose
37
+ self
38
+ end
29
39
 
30
- puts sitemap_index.stats_summary(:time_taken => end_time - start_time) if verbose
40
+ # Dreprecated. Use create.
41
+ def add_links(&block)
42
+ @yield_sitemap = true
43
+ create(&block)
44
+ @yield_sitemap = false
31
45
  end
32
46
 
33
47
  # Constructor
34
48
  #
35
- # Call with a hash of options. Options:
49
+ # == Options:
50
+ # * <tt>:default_host</tt> - host including protocol to use in all sitemap links
51
+ # e.g. http://en.google.ca
36
52
  #
37
- # <tt>public_path</tt> full path to the directory to write sitemaps in.
38
- # Defaults to your Rails <tt>public/</tt> directory.
53
+ # * <tt>:public_path</tt> - Full or relative path to the directory to write sitemaps into.
54
+ # Defaults to the <tt>public/</tt> directory in your application root directory or
55
+ # the current working directory.
39
56
  #
40
- # <tt>sitemaps_path</tt> path fragment within public to write sitemaps
57
+ # * <tt>:sitemaps_host</tt> - host (including protocol) to use in links to the sitemaps. Useful if your sitemaps
58
+ # are hosted o different server e.g. 'http://amazon.aws.com/'
59
+ #
60
+ # * <tt>:sitemaps_path</tt> - path fragment within public to write sitemaps
41
61
  # to e.g. 'en/'. Sitemaps are written to <tt>public_path</tt> + <tt>sitemaps_path</tt>
42
62
  #
43
- # <tt>default_host</tt> host including protocol to use in all sitemap links
44
- # e.g. http://en.google.ca
63
+ # * <tt>:filename</tt> - symbol giving the base name for files (default <tt>:sitemap</tt>).
64
+ # The sitemap names are generated like "#{filename}1.xml.gz", "#{filename}2.xml.gz"
65
+ # and the index name is like "#{filename}_index.xml.gz".
45
66
  #
46
- # <tt>filename</tt> symbol giving the base name for files (default <tt>:sitemap</tt>).
47
- # The sitemap names are generated like "#{@filename}1.xml.gzip", "#{@filename}2.xml.gzip"
48
- # and the index name is like "#{@filename}_index.xml.gzip".
67
+ # * <tt>:sitemaps_namer</tt> - A +SitemapNamer+ instance for generating the sitemap names.
49
68
  #
50
- # <tt>include_root</tt> whether to include the root url i.e. '/' in each group of sitemaps.
51
- # Default is false.
69
+ # * <tt>:include_root</tt> - whether to include the root url i.e. '/' in each group of sitemaps.
70
+ # Default is true.
52
71
  #
53
- # <tt>include_index</tt> whether to include the sitemap index URL in each group of sitemaps.
54
- # Default is false.
55
- def initialize(*args)
56
-
57
- # Extract options
58
- options = if (!args.first.nil? && !args.first.is_a?(Hash)) || args.size > 1
59
- warn "Deprecated. Please call with an options hash instead."
60
- [:public_path, :sitemaps_path, :default_host, :filename].each_with_index.inject({}) do |hash, arg|
61
- hash[arg[0]] = args[arg[1]]
62
- hash
63
- end
64
- else
65
- args.first || {}
66
- end
67
-
68
- # Option defaults
72
+ # * <tt>:include_index</tt> - whether to include the sitemap index URL in each group of sitemaps.
73
+ # Default is true.
74
+ #
75
+ # * <tt>:verbose</tt> - If +true+, output a summary line for each sitemap and sitemap
76
+ # index that is created. Default is +false+.
77
+ def initialize(options={})
69
78
  options.reverse_merge!({
70
79
  :include_root => true,
71
80
  :include_index => true,
72
81
  :filename => :sitemap,
73
- :public_path => SitemapGenerator.app.root + 'public/',
74
- :sitemaps_path => nil
82
+ :verbose => false
75
83
  })
76
84
  options.each_pair { |k, v| instance_variable_set("@#{k}".to_sym, v) }
77
85
 
78
-
79
- # Create a location object to store all the location options
80
- @location = SitemapGenerator::SitemapLocation.new(
81
- :sitemaps_path => @sitemaps_path,
82
- :public_path => @public_path,
83
- :host => @default_host
84
- )
85
- end
86
-
87
- # Entry point for users.
88
- #
89
- # Called within the user's eval'ed sitemap config file. Add links to sitemap files
90
- # passing a block. This instance is passed in as an argument. You can call
91
- # `add` on it to add links.
92
- #
93
- # Example:
94
- # add_links do |sitemap|
95
- # sitemap.add '/'
96
- # end
97
- def add_links
98
- sitemap.add('/', :lastmod => Time.now, :changefreq => 'always', :priority => 1.0, :host => @location.host) if include_root
99
- sitemap.add(sitemap_index, :lastmod => Time.now, :changefreq => 'always', :priority => 1.0) if include_index
100
- yield self
86
+ # If an index is passed in, protect it from modification.
87
+ # Sitemaps can be added to the index but nothing else can be changed.
88
+ if options[:sitemap_index]
89
+ @protect_index = true
90
+ end
101
91
  end
102
92
 
103
93
  # Add a link to a Sitemap. If a new Sitemap is required, one will be created for
@@ -107,15 +97,69 @@ module SitemapGenerator
107
97
  # options - see README.
108
98
  # host - host for the link, defaults to your <tt>default_host</tt>.
109
99
  def add(link, options={})
110
- sitemap.add(link, options.reverse_merge!(:host => @location.host))
100
+ add_default_links if !@added_default_links
101
+ sitemap.add(link, options.reverse_merge!(:host => @default_host))
111
102
  rescue SitemapGenerator::SitemapFullError
112
- finalize_sitemap
103
+ finalize_sitemap!
113
104
  retry
114
105
  rescue SitemapGenerator::SitemapFinalizedError
115
- @sitemap = sitemap.next
106
+ @sitemap = sitemap.new
116
107
  retry
117
108
  end
118
109
 
110
+ # Create a new group of sitemaps. Returns a new LinkSet instance with options set on it.
111
+ #
112
+ # All groups share this LinkSet's sitemap index, which is not modified by any of the options
113
+ # passed to +group+.
114
+ #
115
+ # === Options
116
+ # Any of the options to LinkSet.new. Except for <tt>:public_path</tt> which is shared
117
+ # by all groups.
118
+ #
119
+ # The current options are inherited by the new group of sitemaps. The only exceptions
120
+ # being <tt>:include_index</tt> and <tt>:include_root</tt> which default to +false+.
121
+ #
122
+ # Pass a block to add links to the new LinkSet. If you pass a block the sitemaps will
123
+ # be finalized when the block returns.
124
+ #
125
+ # If you are not changing any of the location settings like <tt>filename<tt>,
126
+ # <tt>sitemaps_path</tt>, <tt>sitemaps_host</tt> or <tt>sitemaps_namer</tt>
127
+ # the current sitemap will be used in the group. All of the options you have
128
+ # specified which affect the way the links are generated will still be applied
129
+ # for the duration of the group.
130
+ def group(opts={}, &block)
131
+ @created_group = true
132
+ original_opts = opts.dup
133
+
134
+ if (@@requires_finalization_opts & original_opts.keys).empty?
135
+ # If no new filename or path is specified reuse the default sitemap file.
136
+ # A new location object will be set on it for the duration of the group.
137
+ opts[:sitemap] = sitemap
138
+ elsif original_opts.key?(:sitemaps_host) && (@@new_location_opts & original_opts.keys).empty?
139
+ # If no location options are provided we are creating the next sitemap in the
140
+ # current series, so finalize and inherit the namer.
141
+ finalize_sitemap!
142
+ opts[:sitemaps_namer] = sitemaps_namer
143
+ end
144
+
145
+ opts = options_for_group(opts)
146
+ @group = SitemapGenerator::LinkSet.new(opts)
147
+ if opts.key?(:sitemap)
148
+ # If the group is sharing the current sitemap, set the
149
+ # new location options on the location object.
150
+ @original_location = @sitemap.location.dup
151
+ @sitemap.location.merge!(@group.sitemap_location)
152
+ if block_given?
153
+ @group.interpreter.eval(:yield_sitemap => @yield_sitemap || SitemapGenerator.yield_sitemap?, &block)
154
+ @sitemap.location.merge!(@original_location)
155
+ end
156
+ elsif block_given?
157
+ @group.interpreter.eval(:yield_sitemap => @yield_sitemap || SitemapGenerator.yield_sitemap?, &block)
158
+ @group.finalize_sitemap!
159
+ end
160
+ @group
161
+ end
162
+
119
163
  # Ping search engines.
120
164
  #
121
165
  # @see http://en.wikipedia.org/wiki/Sitemap_index
@@ -135,7 +179,9 @@ module SitemapGenerator
135
179
  search_engines.each do |engine, link|
136
180
  next if engine == :yahoo && !self.yahoo_app_id
137
181
  begin
138
- open(link)
182
+ Timeout::timeout(10) {
183
+ open(link)
184
+ }
139
185
  puts "Successful ping of #{engine.to_s.titleize}" if verbose
140
186
  rescue Timeout::Error, StandardError => e
141
187
  puts "Ping failed for #{engine.to_s.titleize}: #{e.inspect} (URL #{link})" if verbose
@@ -159,84 +205,218 @@ module SitemapGenerator
159
205
  sitemap_index.total_link_count
160
206
  end
161
207
 
162
- # Set the host name, including protocol, that will be used by default on each
163
- # of your sitemap links. You can pass a different host in your options to `add`
164
- # if you need to change it on a per-link basis.
165
- def default_host=(value)
166
- update_location_info(:host, value)
208
+ # Return the host to use in links to the sitemap files. This defaults to your
209
+ # +default_host+.
210
+ def sitemaps_host
211
+ @sitemaps_host || @default_host
167
212
  end
168
213
 
169
- # Set the public_path. This path gives the location of your public directory.
170
- # The default is the public/ directory in your Rails root. Or if Rails is not
171
- # found, it defaults to public/ in the current directory (of the process).
172
- #
173
- # Example: 'tmp/' if you don't want to generate in public for some reason.
174
- #
175
- # Set to nil to use the current directory.
176
- def public_path=(value)
177
- update_location_info(:public_path, value)
214
+ # Lazy-initialize a sitemap instance when it's accessed
215
+ def sitemap
216
+ @sitemap ||= SitemapGenerator::Builder::SitemapFile.new(sitemap_location)
178
217
  end
179
218
 
180
- # Set the sitemaps_path. This path gives the location to write sitemaps to
181
- # relative to your public_path.
182
- # Example: 'sitemaps/' to generate your sitemaps in 'public/sitemaps/'.
183
- def sitemaps_path=(value)
184
- update_location_info(:sitemaps_path, value)
219
+ # Lazy-initialize a sitemap index instance when it's accessed
220
+ def sitemap_index
221
+ @sitemap_index ||= SitemapGenerator::Builder::SitemapIndexFile.new(sitemap_index_location)
185
222
  end
186
223
 
187
- # Set the filename base to use when generating sitemaps and sitemap indexes.
188
- def filename=(value)
189
- @filename = value
190
- update_sitemap_info(:filename, value)
224
+ def finalize!
225
+ finalize_sitemap!
226
+ finalize_sitemap_index!
191
227
  end
192
228
 
193
- # Lazy-initialize a sitemap instance when it's accessed
194
- def sitemap
195
- @sitemap ||= SitemapGenerator::Builder::SitemapFile.new(
196
- :location => @location.dup,
197
- :filename => @filename
198
- )
229
+ protected
230
+
231
+ # Set each option on this instance using accessor methods. This will affect
232
+ # both the sitemap and the sitemap index.
233
+ def set_options(opts={})
234
+ opts.each_pair do |key, value|
235
+ send("#{key}=", value)
236
+ end
199
237
  end
200
238
 
201
- # Lazy-initialize a sitemap index instance when it's accessed
202
- def sitemap_index
203
- @sitemap_index ||= SitemapGenerator::Builder::SitemapIndexFile.new(
204
- :location => @location.dup,
205
- :filename => "#{@filename}_index"
239
+ # Given +opts+, return a hash of options prepped for creating a new group from this LinkSet.
240
+ # If <tt>:public_path</tt> is present in +opts+ it is removed because groups cannot
241
+ # change the public path.
242
+ def options_for_group(opts)
243
+ opts.delete(:public_path)
244
+ opts.reverse_merge!(
245
+ :include_index => false,
246
+ :include_root => false,
247
+ :sitemap_index => sitemap_index
206
248
  )
249
+
250
+ # Reverse merge the current settings
251
+ current_settings = [
252
+ :include_root,
253
+ :include_index,
254
+ :sitemaps_path,
255
+ :public_path,
256
+ :sitemaps_host,
257
+ :verbose,
258
+ :default_host
259
+ ].inject({}) do |hash, key|
260
+ if value = instance_variable_get(:"@#{key}")
261
+ hash[key] = value
262
+ end
263
+ hash
264
+ end
265
+ opts.reverse_merge!(current_settings)
266
+ opts
207
267
  end
208
268
 
209
- protected
269
+ # Add default links if those options are turned on. Record the fact that we have done so
270
+ # in an instance variable.
271
+ def add_default_links
272
+ sitemap.add('/', :lastmod => Time.now, :changefreq => 'always', :priority => 1.0, :host => @default_host) if include_root
273
+ sitemap.add(sitemap_index, :lastmod => Time.now, :changefreq => 'always', :priority => 1.0) if include_index
274
+ @added_default_links = true
275
+ end
210
276
 
211
277
  # Finalize a sitemap by including it in the index and outputting a summary line.
212
278
  # Do nothing if it has already been finalized.
213
- def finalize_sitemap
214
- return if sitemap.finalized?
279
+ #
280
+ # Don't finalize if the sitemap is empty and a group has been created. The reason
281
+ # being that the group will have written out its sitemap.
282
+ #
283
+ # Add the default links if they have not been added yet and no groups have been created.
284
+ # If the default links haven't been added we know that the sitemap is empty,
285
+ # because they are added on the first call to add(). This ensure that if the
286
+ # block passed to create() is empty the default links are still included in the
287
+ # sitemap.
288
+ def finalize_sitemap!
289
+ add_default_links if !@added_default_links && !@created_group
290
+ return if sitemap.finalized? || sitemap.empty? && @created_group
215
291
  sitemap_index.add(sitemap)
216
292
  puts sitemap.summary if verbose
217
293
  end
218
294
 
219
295
  # Finalize a sitemap index and output a summary line. Do nothing if it has already
220
296
  # been finalized.
221
- def finalize_sitemap_index
222
- return if sitemap_index.finalized?
297
+ def finalize_sitemap_index!
298
+ return if @protect_index || sitemap_index.finalized?
223
299
  sitemap_index.finalize!
224
300
  puts sitemap_index.summary if verbose
225
301
  end
226
302
 
227
- # Update the given attribute on the current sitemap index and sitemap files. But
228
- # don't create the index or sitemap files yet if they are not already created.
229
- def update_sitemap_info(attribute, value)
230
- sitemap_index.send("#{attribute}=", value) if @sitemap_index && !@sitemap_index.finalized?
231
- sitemap.send("#{attribute}=", value) if @sitemap && !@sitemap.finalized?
303
+ # Return the interpreter linked to this instance.
304
+ def interpreter
305
+ require 'sitemap_generator/interpreter'
306
+ @interpreter ||= SitemapGenerator::Interpreter.new(:link_set => self)
232
307
  end
233
308
 
234
- # Update the given attribute on the current sitemap index and sitemap file location objects.
235
- # But don't create the index or sitemap files yet if they are not already created.
236
- def update_location_info(attribute, value)
237
- @location.merge!(attribute => value)
238
- sitemap_index.location.merge!(attribute => value) if @sitemap_index && !@sitemap_index.finalized?
239
- sitemap.location.merge!(attribute => value) if @sitemap && !@sitemap.finalized?
309
+ module LocationHelpers
310
+ public
311
+
312
+ # Set the host name, including protocol, that will be used by default on each
313
+ # of your sitemap links. You can pass a different host in your options to `add`
314
+ # if you need to change it on a per-link basis.
315
+ def default_host=(value)
316
+ @default_host = value
317
+ update_location_info(:host, value)
318
+ end
319
+
320
+ # Set the public_path. This path gives the location of your public directory.
321
+ # The default is the public/ directory in your Rails root. Or if Rails is not
322
+ # found, it defaults to public/ in the current directory (of the process).
323
+ #
324
+ # Example: 'tmp/' if you don't want to generate in public for some reason.
325
+ #
326
+ # Set to nil to use the current directory.
327
+ def public_path=(value)
328
+ @public_path = Pathname.new(value.to_s)
329
+ @public_path = SitemapGenerator.app.root + @public_path if @public_path.relative?
330
+ update_location_info(:public_path, @public_path)
331
+ @public_path
332
+ end
333
+
334
+ # Return a Pathname with the full path to the public directory
335
+ def public_path
336
+ @public_path ||= self.send(:public_path=, 'public/')
337
+ end
338
+
339
+ # Set the sitemaps_path. This path gives the location to write sitemaps to
340
+ # relative to your public_path.
341
+ # Example: 'sitemaps/' to generate your sitemaps in 'public/sitemaps/'.
342
+ def sitemaps_path=(value)
343
+ @sitemaps_path = value
344
+ update_location_info(:sitemaps_path, value)
345
+ end
346
+
347
+ # Set the host name, including protocol, that will be used on all links to your sitemap
348
+ # files. Useful when the server that hosts the sitemaps is not on the same host as
349
+ # the links in the sitemap.
350
+ def sitemaps_host=(value)
351
+ @sitemaps_host = value
352
+ update_location_info(:host, value)
353
+ end
354
+
355
+ # Set the filename base to use when generating sitemaps and sitemap indexes.
356
+ # The index name will be +value+ with <tt>_index.xml.gz</tt> appended.
357
+ # === Example
358
+ # <tt>filename = :sitemap</tt>
359
+ def filename=(value)
360
+ @filename = value
361
+ self.sitemaps_namer = SitemapGenerator::SitemapNamer.new(@filename)
362
+ self.sitemap_index_namer = SitemapGenerator::SitemapIndexNamer.new("#{@filename}_index")
363
+ end
364
+
365
+ # Set the namer to use when generating SitemapFiles (does not apply to the
366
+ # SitemapIndexFile)
367
+ def sitemaps_namer=(value)
368
+ @sitemaps_namer = value
369
+ @sitemap.location[:namer] = value if @sitemap && !@sitemap.finalized?
370
+ end
371
+
372
+ # Return the current sitemaps namer object. If it not set, looks for it on
373
+ # the current sitemap and if there is no sitemap, creates a new one using
374
+ # the current filename.
375
+ def sitemaps_namer
376
+ @sitemaps_namer ||= @sitemap && @sitemap.location.namer || SitemapGenerator::SitemapNamer.new(@filename)
377
+ end
378
+
379
+ # Set the namer to use when generating SitemapFiles (does not apply to the
380
+ # SitemapIndexFile)
381
+ def sitemap_index_namer=(value)
382
+ @sitemap_index_namer = value
383
+ @sitemap_index.location[:namer] = value if @sitemap_index && !@sitemap_index.finalized? && !@protect_index
384
+ end
385
+
386
+ def sitemap_index_namer
387
+ @sitemap_index_namer ||= @sitemap_index && @sitemap_index.location.namer || SitemapGenerator::SitemapIndexNamer.new("#{@filename}_index")
388
+ end
389
+
390
+ # Return a new +SitemapLocation+ instance with the current options included
391
+ def sitemap_location
392
+ SitemapGenerator::SitemapLocation.new(
393
+ :host => sitemaps_host,
394
+ :namer => sitemaps_namer,
395
+ :public_path => public_path,
396
+ :sitemaps_path => @sitemaps_path
397
+ )
398
+ end
399
+
400
+ # Return a new +SitemapIndexLocation+ instance with the current options included
401
+ def sitemap_index_location
402
+ SitemapGenerator::SitemapLocation.new(
403
+ :host => sitemaps_host,
404
+ :namer => sitemap_index_namer,
405
+ :public_path => public_path,
406
+ :sitemaps_path => @sitemaps_path
407
+ )
408
+ end
409
+
410
+ protected
411
+
412
+ # Update the given attribute on the current sitemap index and sitemap file location objects.
413
+ # But don't create the index or sitemap files yet if they are not already created.
414
+ def update_location_info(attribute, value, opts={})
415
+ opts.reverse_merge!(:include_index => !@protect_index)
416
+ @sitemap_index.location[attribute] = value if opts[:include_index] && @sitemap_index && !@sitemap_index.finalized?
417
+ @sitemap.location[attribute] = value if @sitemap && !@sitemap.finalized?
418
+ end
240
419
  end
420
+ include LocationHelpers
241
421
  end
242
- end
422
+ end