sitemap_generator 6.3.0 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +107 -61
  3. data/README.md +176 -171
  4. data/VERSION +1 -1
  5. data/lib/capistrano/sitemap_generator.rb +2 -0
  6. data/lib/sitemap_generator/adapters/active_storage_adapter.rb +31 -0
  7. data/lib/sitemap_generator/adapters/aws_sdk_adapter.rb +29 -13
  8. data/lib/sitemap_generator/adapters/file_adapter.rb +12 -13
  9. data/lib/sitemap_generator/adapters/fog_adapter.rb +9 -7
  10. data/lib/sitemap_generator/adapters/google_storage_adapter.rb +8 -5
  11. data/lib/sitemap_generator/adapters/s3_adapter.rb +23 -18
  12. data/lib/sitemap_generator/adapters/wave_adapter.rb +8 -6
  13. data/lib/sitemap_generator/application.rb +9 -3
  14. data/lib/sitemap_generator/builder/sitemap_file.rb +41 -31
  15. data/lib/sitemap_generator/builder/sitemap_index_file.rb +31 -23
  16. data/lib/sitemap_generator/builder/sitemap_index_url.rb +10 -9
  17. data/lib/sitemap_generator/builder/sitemap_url.rb +130 -80
  18. data/lib/sitemap_generator/builder.rb +7 -3
  19. data/lib/sitemap_generator/core_ext/big_decimal.rb +40 -33
  20. data/lib/sitemap_generator/core_ext/numeric.rb +52 -46
  21. data/lib/sitemap_generator/core_ext.rb +2 -0
  22. data/lib/sitemap_generator/helpers/number_helper.rb +84 -76
  23. data/lib/sitemap_generator/interpreter.rb +22 -18
  24. data/lib/sitemap_generator/link_set.rb +114 -113
  25. data/lib/sitemap_generator/railtie.rb +4 -1
  26. data/lib/sitemap_generator/simple_namer.rb +8 -6
  27. data/lib/sitemap_generator/sitemap_location.rb +52 -40
  28. data/lib/sitemap_generator/tasks.rb +16 -26
  29. data/lib/sitemap_generator/templates.rb +11 -9
  30. data/lib/sitemap_generator/utilities.rb +46 -24
  31. data/lib/sitemap_generator.rb +42 -27
  32. data/lib/tasks/sitemap_generator_tasks.rake +2 -0
  33. data/rails/install.rb +2 -0
  34. data/rails/uninstall.rb +2 -0
  35. data/templates/sitemap.rb +3 -1
  36. metadata +7 -131
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.3.0
1
+ 7.1.1
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  load File.expand_path(File.join('..', 'tasks', 'sitemap_generator.cap'), __FILE__)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SitemapGenerator
4
+ # Class for uploading sitemaps to ActiveStorage.
5
+ class ActiveStorageAdapter
6
+ attr_reader :key, :filename
7
+
8
+ def initialize(key: :sitemap, filename: 'sitemap.xml.gz')
9
+ @key = key
10
+ @filename = filename
11
+ end
12
+
13
+ def write(location, raw_data) # rubocop:disable Metrics/MethodLength
14
+ SitemapGenerator::FileAdapter.new.write(location, raw_data)
15
+
16
+ ActiveStorage::Blob.transaction do
17
+ ActiveStorage::Blob.where(key: key).destroy_all
18
+
19
+ File.open(location.path, 'rb') do |io|
20
+ ActiveStorage::Blob.create_and_upload!(
21
+ key: key,
22
+ io: io,
23
+ filename: filename,
24
+ content_type: 'application/gzip',
25
+ identify: false
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,6 +1,8 @@
1
- if !defined?(Aws::S3::Resource) or !defined?(Aws::Credentials)
2
- raise LoadError, "Error: `Aws::S3::Resource` and/or `Aws::Credentials` are not defined.\n\n"\
3
- "Please `require 'aws-sdk'` - or another library that defines these classes."
1
+ # frozen_string_literal: true
2
+
3
+ if !defined?(Aws::S3::Resource) || !defined?(Aws::Credentials)
4
+ raise LoadError, "Error: `Aws::S3::Resource` and/or `Aws::Credentials` are not defined.\n\n" \
5
+ "Please `require 'aws-sdk'` - or another library that defines these classes."
4
6
  end
5
7
 
6
8
  module SitemapGenerator
@@ -22,32 +24,46 @@ module SitemapGenerator
22
24
  # **Deprecated, use :secret_access_key instead** :aws_secret_access_key [String] Your AWS secret access key
23
25
  # **Deprecated, use :region instead** :aws_region [String] Your AWS region
24
26
  # :acl [String] The ACL to apply to the uploaded files. Defaults to 'public-read'.
25
- # :cache_control [String] The cache control headder to apply to the uploaded files. Defaults to 'private, max-age=0, no-cache'.
27
+ # :cache_control [String] The cache control headder to apply to the uploaded files.
28
+ # Defaults to 'private, max-age=0, no-cache'.
26
29
  #
27
30
  # All other options you provide are passed directly to the AWS client.
28
31
  # See https://docs.aws.amazon.com/sdk-for-ruby/v2/api/Aws/S3/Client.html#initialize-instance_method
29
32
  # for a full list of supported options.
30
- def initialize(bucket, aws_access_key_id: nil, aws_secret_access_key: nil, aws_region: nil, aws_endpoint: nil, acl: 'public-read', cache_control: 'private, max-age=0, no-cache', **options)
33
+ def initialize(bucket, aws_access_key_id: nil, aws_secret_access_key: nil, aws_session_token: nil, aws_region: nil, # rubocop:disable Metrics/ParameterLists
34
+ aws_endpoint: nil, acl: 'public-read', cache_control: 'private, max-age=0, no-cache', **options)
31
35
  @bucket = bucket
32
36
  @acl = acl
33
37
  @cache_control = cache_control
34
38
  @options = options
35
39
  set_option_unless_set(:access_key_id, aws_access_key_id)
36
40
  set_option_unless_set(:secret_access_key, aws_secret_access_key)
41
+ set_option_unless_set(:session_token, aws_session_token)
37
42
  set_option_unless_set(:region, aws_region)
38
43
  set_option_unless_set(:endpoint, aws_endpoint)
39
44
  end
40
45
 
41
-
42
46
  # Call with a SitemapLocation and string data
43
- def write(location, raw_data)
47
+ def write(location, raw_data) # rubocop:disable Metrics/MethodLength
44
48
  SitemapGenerator::FileAdapter.new.write(location, raw_data)
45
- s3_object = s3_resource.bucket(@bucket).object(location.path_in_public)
46
- s3_object.upload_file(location.path, {
47
- acl: @acl,
48
- cache_control: @cache_control,
49
- content_type: location[:compress] ? 'application/x-gzip' : 'application/xml'
50
- }.compact)
49
+
50
+ if defined?(Aws::S3::TransferManager)
51
+ client = Aws::S3::Client.new(@options)
52
+ transfer_manager = Aws::S3::TransferManager.new(client: client)
53
+ transfer_manager.upload_file(location.path,
54
+ bucket: @bucket,
55
+ key: location.path_in_public,
56
+ acl: @acl,
57
+ cache_control: @cache_control,
58
+ content_type: location.content_type)
59
+ else
60
+ s3_object = s3_resource.bucket(@bucket).object(location.path_in_public)
61
+ s3_object.upload_file(location.path, {
62
+ acl: @acl,
63
+ cache_control: @cache_control,
64
+ content_type: location.content_type
65
+ }.compact)
66
+ end
51
67
  end
52
68
 
53
69
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SitemapGenerator
2
4
  # Class for writing out data to a file.
3
5
  class FileAdapter
@@ -8,23 +10,26 @@ module SitemapGenerator
8
10
  # compressed prior to being written out. Otherwise the data will be written out
9
11
  # unchanged.
10
12
  # @param raw_data - data to write to the file.
11
- def write(location, raw_data)
13
+ def write(location, raw_data) # rubocop:disable Metrics/MethodLength
12
14
  # Ensure that the directory exists
13
15
  dir = location.directory
14
16
  if !File.exist?(dir)
15
17
  FileUtils.mkdir_p(dir)
16
18
  elsif !File.directory?(dir)
17
- raise SitemapError.new("#{dir} should be a directory!")
19
+ raise SitemapError, "#{dir} should be a directory!"
18
20
  end
19
21
 
20
- stream = open(location.path, 'wb')
21
- if location.path.to_s =~ /.gz$/
22
- gzip(stream, raw_data)
23
- else
24
- plain(stream, raw_data)
22
+ File.open(location.path, 'wb') do |stream|
23
+ if location.gzip?
24
+ gzip(stream, raw_data)
25
+ else
26
+ stream.write raw_data
27
+ end
25
28
  end
26
29
  end
27
30
 
31
+ private
32
+
28
33
  # Write `data` to a stream, passing the data through a GzipWriter
29
34
  # to compress it.
30
35
  def gzip(stream, data)
@@ -32,11 +37,5 @@ module SitemapGenerator
32
37
  gz.write data
33
38
  gz.close
34
39
  end
35
-
36
- # Write `data` to a stream as is.
37
- def plain(stream, data)
38
- stream.write data
39
- stream.close
40
- end
41
40
  end
42
41
  end
@@ -1,6 +1,8 @@
1
- if !defined?(Fog::Storage)
2
- raise LoadError, "Error: `Fog::Storage` is not defined.\n\n"\
3
- "Please `require 'fog'` - or another library that defines this class."
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Fog::Storage)
4
+ raise LoadError, "Error: `Fog::Storage` is not defined.\n\n" \
5
+ "Please `require 'fog'` - or another library that defines this class."
4
6
  end
5
7
 
6
8
  module SitemapGenerator
@@ -21,11 +23,11 @@ module SitemapGenerator
21
23
  SitemapGenerator::FileAdapter.new.write(location, raw_data)
22
24
 
23
25
  storage = Fog::Storage.new(@fog_credentials)
24
- directory = storage.directories.new(:key => @fog_directory)
26
+ directory = storage.directories.new(key: @fog_directory)
25
27
  directory.files.create(
26
- :key => location.path_in_public,
27
- :body => File.open(location.path),
28
- :public => true
28
+ key: location.path_in_public,
29
+ body: File.open(location.path),
30
+ public: true
29
31
  )
30
32
  end
31
33
  end
@@ -1,6 +1,8 @@
1
- if !defined?(Google::Cloud::Storage)
2
- raise LoadError, "Error: `Google::Cloud::Storage` is not defined.\n\n"\
3
- "Please `require 'google/cloud/storage'` - or another library that defines this class."
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Google::Cloud::Storage)
4
+ raise LoadError, "Error: `Google::Cloud::Storage` is not defined.\n\n" \
5
+ "Please `require 'google/cloud/storage'` - or another library that defines this class."
4
6
  end
5
7
 
6
8
  module SitemapGenerator
@@ -10,7 +12,8 @@ module SitemapGenerator
10
12
  #
11
13
  # @param [Hash] opts Google::Cloud::Storage configuration options.
12
14
  # @option :bucket [String] Required. Name of Google Storage Bucket where the file is to be uploaded.
13
- # @option :acl [String] Optional. Access control which is applied to the uploaded file(s). Default value is 'public'.
15
+ # @option :acl [String] Optional. Access control which is applied to the uploaded file(s).
16
+ # Default value is 'public'.
14
17
  #
15
18
  # All options other than the `:bucket` and `:acl` options are passed to the `Google::Cloud::Storage.new`
16
19
  # initializer. See https://googleapis.dev/ruby/google-cloud-storage/latest/file.AUTHENTICATION.html
@@ -23,7 +26,7 @@ module SitemapGenerator
23
26
  def initialize(opts = {})
24
27
  opts = opts.clone
25
28
  @bucket = opts.delete(:bucket)
26
- @acl = opts.has_key?(:acl) ? opts.delete(:acl) : 'public'
29
+ @acl = opts.key?(:acl) ? opts.delete(:acl) : 'public'
27
30
  @storage_options = opts
28
31
  end
29
32
 
@@ -1,6 +1,8 @@
1
- if !defined?(Fog::Storage)
2
- raise LoadError, "Error: `Fog::Storage` is not defined.\n\n"\
3
- "Please `require 'fog-aws'` - or another library that defines this class."
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Fog::Storage)
4
+ raise LoadError, "Error: `Fog::Storage` is not defined.\n\n" \
5
+ "Please `require 'fog-aws'` - or another library that defines this class."
4
6
  end
5
7
 
6
8
  module SitemapGenerator
@@ -20,27 +22,29 @@ module SitemapGenerator
20
22
  #
21
23
  # Alternatively you can use an environment variable to configure each option (except `fog_storage_options`).
22
24
  # The environment variables have the same name but capitalized, e.g. `FOG_PATH_STYLE`.
23
- def initialize(opts = {})
24
- @aws_access_key_id = opts[:aws_access_key_id] || ENV['AWS_ACCESS_KEY_ID']
25
- @aws_secret_access_key = opts[:aws_secret_access_key] || ENV['AWS_SECRET_ACCESS_KEY']
26
- @fog_provider = opts[:fog_provider] || ENV['FOG_PROVIDER']
27
- @fog_directory = opts[:fog_directory] || ENV['FOG_DIRECTORY']
28
- @fog_region = opts[:fog_region] || ENV['FOG_REGION']
29
- @fog_path_style = opts[:fog_path_style] || ENV['FOG_PATH_STYLE']
25
+ def initialize(opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
26
+ @aws_access_key_id = opts[:aws_access_key_id] || ENV.fetch('AWS_ACCESS_KEY_ID', nil)
27
+ @aws_secret_access_key = opts[:aws_secret_access_key] || ENV.fetch('AWS_SECRET_ACCESS_KEY', nil)
28
+ @aws_session_token = opts[:aws_session_token] || ENV.fetch('AWS_SESSION_TOKEN', nil)
29
+ @fog_provider = opts[:fog_provider] || ENV.fetch('FOG_PROVIDER', nil)
30
+ @fog_directory = opts[:fog_directory] || ENV.fetch('FOG_DIRECTORY', nil)
31
+ @fog_region = opts[:fog_region] || ENV.fetch('FOG_REGION', nil)
32
+ @fog_path_style = opts[:fog_path_style] || ENV.fetch('FOG_PATH_STYLE', nil)
30
33
  @fog_storage_options = opts[:fog_storage_options] || {}
31
- fog_public = opts[:fog_public].nil? ? ENV['FOG_PUBLIC'] : opts[:fog_public]
32
- @fog_public = SitemapGenerator::Utilities.falsy?(fog_public) ? false : true
34
+ fog_public = opts[:fog_public].nil? ? ENV.fetch('FOG_PUBLIC', nil) : opts[:fog_public]
35
+ @fog_public = !SitemapGenerator::Utilities.falsy?(fog_public)
33
36
  end
34
37
 
35
38
  # Call with a SitemapLocation and string data
36
- def write(location, raw_data)
39
+ def write(location, raw_data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
37
40
  SitemapGenerator::FileAdapter.new.write(location, raw_data)
38
41
 
39
- credentials = { :provider => @fog_provider }
42
+ credentials = { provider: @fog_provider }
40
43
 
41
44
  if @aws_access_key_id && @aws_secret_access_key
42
45
  credentials[:aws_access_key_id] = @aws_access_key_id
43
46
  credentials[:aws_secret_access_key] = @aws_secret_access_key
47
+ credentials[:aws_session_token] = @aws_session_token if @aws_session_token
44
48
  else
45
49
  credentials[:use_iam_profile] = true
46
50
  end
@@ -49,11 +53,12 @@ module SitemapGenerator
49
53
  credentials[:path_style] = @fog_path_style if @fog_path_style
50
54
 
51
55
  storage = Fog::Storage.new(@fog_storage_options.merge(credentials))
52
- directory = storage.directories.new(:key => @fog_directory)
56
+ directory = storage.directories.new(key: @fog_directory)
53
57
  directory.files.create(
54
- :key => location.path_in_public,
55
- :body => File.open(location.path),
56
- :public => @fog_public
58
+ key: location.path_in_public,
59
+ body: File.open(location.path),
60
+ public: @fog_public,
61
+ content_type: location.content_type
57
62
  )
58
63
  end
59
64
  end
@@ -1,6 +1,8 @@
1
- if !defined?(::CarrierWave::Uploader::Base)
2
- raise LoadError, "Error: `CarrierWave::Uploader::Base` is not defined.\n\n"\
3
- "Please `require 'carrierwave'` - or another library that defines this class."
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(CarrierWave::Uploader::Base)
4
+ raise LoadError, "Error: `CarrierWave::Uploader::Base` is not defined.\n\n" \
5
+ "Please `require 'carrierwave'` - or another library that defines this class."
4
6
  end
5
7
 
6
8
  module SitemapGenerator
@@ -12,10 +14,10 @@ module SitemapGenerator
12
14
  def write(location, raw_data)
13
15
  SitemapGenerator::FileAdapter.new.write(location, raw_data)
14
16
  directory = File.dirname(location.path_in_public)
15
- if directory != '.'
16
- self.store_dir = directory
17
+ self.store_dir = directory if directory != '.'
18
+ File.open(location.path, 'rb') do |io|
19
+ store!(io)
17
20
  end
18
- store!(open(location.path, 'rb'))
19
21
  end
20
22
  end
21
23
  end
@@ -1,17 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
 
3
5
  module SitemapGenerator
6
+ # Thin abstraction over the host application environment.
7
+ # Provides the app +root+ path and Rails version detection.
4
8
  class Application
5
- def is_rails?
9
+ def is_rails? # rubocop:disable Naming/PredicatePrefix
6
10
  !!defined?(Rails::VERSION)
7
11
  end
8
12
 
9
13
  # Returns a boolean indicating whether this environment is Rails 3
10
14
  #
11
15
  # @return [Boolean]
12
- def is_at_least_rails3?
16
+ def is_at_least_rails3? # rubocop:disable Naming/PredicatePrefix
13
17
  is_rails? && Rails.version.to_f >= 3
14
- rescue
18
+ rescue StandardError
15
19
  false # Rails.version defined in 2.1.0
16
20
  end
17
21
 
@@ -29,6 +33,7 @@ module SitemapGenerator
29
33
  def rails_root
30
34
  return ::Rails.root.to_s if defined?(::Rails.root) && ::Rails.root
31
35
  return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
36
+
32
37
  nil
33
38
  end
34
39
 
@@ -40,6 +45,7 @@ module SitemapGenerator
40
45
  def rails_env
41
46
  return ::Rails.env.to_s if defined?(::Rails.env)
42
47
  return RAILS_ENV.to_s if defined?(RAILS_ENV)
48
+
43
49
  nil
44
50
  end
45
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
  require 'fileutils'
3
5
  require 'sitemap_generator/helpers/number_helper'
@@ -9,10 +11,12 @@ module SitemapGenerator
9
11
  #
10
12
  # sitemap = SitemapFile.new(:location => SitemapLocation.new(...))
11
13
  # sitemap.add('/', { ... }) <- add a link to the sitemap
12
- # sitemap.finalize! <- write the sitemap file and freeze the object to protect it from further modification
14
+ # sitemap.finalize! <- write the sitemap file and freeze the object
15
+ # to protect it from further modification
13
16
  #
14
17
  class SitemapFile
15
18
  include SitemapGenerator::Helpers::NumberHelper
19
+
16
20
  attr_reader :link_count, :filesize, :location, :news_count
17
21
 
18
22
  # === Options
@@ -20,12 +24,14 @@ module SitemapGenerator
20
24
  # * <tt>location</tt> - a SitemapGenerator::SitemapLocation instance or a Hash of options
21
25
  # from which a SitemapLocation will be created for you. See `SitemapGenerator::SitemapLocation` for
22
26
  # the supported list of options.
23
- def initialize(opts={})
27
+ def initialize(opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
24
28
  @location = opts.is_a?(Hash) ? SitemapGenerator::SitemapLocation.new(opts) : opts
25
29
  @link_count = 0
26
30
  @news_count = 0
27
- @xml_content = '' # XML urlset content
28
- @xml_wrapper_start = <<-HTML
31
+ # The + prefix makes this string explicitly mutable. Ruby 2.x with
32
+ # --enable=frozen-string-literal freezes interpolated heredocs, and
33
+ # gsub! below requires a mutable string.
34
+ @xml_wrapper_start = +<<-HTML
29
35
  <?xml version="1.0" encoding="UTF-8"?>
30
36
  <urlset
31
37
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -41,33 +47,34 @@ module SitemapGenerator
41
47
  >
42
48
  HTML
43
49
  @xml_wrapper_start.gsub!(/\s+/, ' ').gsub!(/ *> */, '>').strip!
44
- @xml_wrapper_end = %q[</urlset>]
45
- @filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) + SitemapGenerator::Utilities.bytesize(@xml_wrapper_end)
50
+ @xml_wrapper_end = '</urlset>'
51
+ @filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) +
52
+ SitemapGenerator::Utilities.bytesize(@xml_wrapper_end)
53
+ @xml_content = @xml_wrapper_start
46
54
  @written = false
47
55
  @reserved_name = nil # holds the name reserved from the namer
48
56
  @frozen = false # rather than actually freeze, use this boolean
49
57
  end
50
58
 
51
- # If a name has been reserved, use the last modified time from the file.
52
- # Otherwise return nil. We don't want to prematurely assign a name
53
- # for this sitemap if one has not yet been reserved, because we may
54
- # mess up the name-assignment sequence.
59
+ # Return the time the sitemap was written, or nil if it has not been written yet.
60
+ # Returning nil before write prevents prematurely reserving a name in the
61
+ # namer sequence.
55
62
  def lastmod
56
- File.mtime(location.path) if location.reserved_name?
57
- rescue
58
- nil
63
+ @lastmod if location.reserved_name?
59
64
  end
60
65
 
61
66
  def empty?
62
- @link_count == 0
67
+ @link_count.zero?
63
68
  end
64
69
 
65
70
  # Return a boolean indicating whether the sitemap file can fit another link
66
71
  # of <tt>bytes</tt> bytes in size. You can also pass a string and the
67
72
  # bytesize will be calculated for you.
68
73
  def file_can_fit?(bytes)
69
- bytes = bytes.is_a?(String) ? SitemapGenerator::Utilities.bytesize(bytes) : bytes
70
- (@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && @link_count < max_sitemap_links && @news_count < SitemapGenerator::MAX_SITEMAP_NEWS
74
+ bytes = SitemapGenerator::Utilities.bytesize(bytes) if bytes.is_a?(String)
75
+ (@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE &&
76
+ @link_count < max_sitemap_links &&
77
+ @news_count < SitemapGenerator::MAX_SITEMAP_NEWS
71
78
  end
72
79
 
73
80
  # Add a link to the sitemap file.
@@ -89,22 +96,21 @@ module SitemapGenerator
89
96
  #
90
97
  # The link added to the sitemap will use the host from its location object
91
98
  # if no host has been specified.
92
- def add(link, options={})
99
+ def add(link, options = {}) # rubocop:disable Metrics/MethodLength
93
100
  raise SitemapGenerator::SitemapFinalizedError if finalized?
94
101
 
95
- sitemap_url = if link.is_a?(SitemapUrl)
96
- link
97
- else
98
- options[:host] ||= @location.host
99
- SitemapUrl.new(link, options)
100
- end
102
+ sitemap_url =
103
+ if link.is_a?(SitemapUrl)
104
+ link
105
+ else
106
+ options[:host] ||= @location.host
107
+ SitemapUrl.new(link, options)
108
+ end
101
109
 
102
110
  xml = sitemap_url.to_xml
103
- raise SitemapGenerator::SitemapFullError if !file_can_fit?(xml)
111
+ raise SitemapGenerator::SitemapFullError unless file_can_fit?(xml)
104
112
 
105
- if sitemap_url.news?
106
- @news_count += 1
107
- end
113
+ @news_count += 1 if sitemap_url.news?
108
114
 
109
115
  # Add the XML to the sitemap
110
116
  @xml_content << xml
@@ -118,6 +124,7 @@ module SitemapGenerator
118
124
  # has already been finalized.
119
125
  def finalize!
120
126
  raise SitemapGenerator::SitemapFinalizedError if finalized?
127
+
121
128
  @frozen = true
122
129
  end
123
130
 
@@ -133,11 +140,14 @@ module SitemapGenerator
133
140
  # A SitemapGenerator::SitemapError exception is raised if the file has
134
141
  # already been written.
135
142
  def write
136
- raise SitemapGenerator::SitemapError.new("Sitemap already written!") if written?
143
+ raise SitemapGenerator::SitemapError, 'Sitemap already written!' if written?
144
+
137
145
  finalize! unless finalized?
138
146
  reserve_name
139
- @location.write(@xml_wrapper_start + @xml_content + @xml_wrapper_end, link_count)
140
- @xml_content = @xml_wrapper_start = @xml_wrapper_end = ''
147
+ @xml_content << @xml_wrapper_end
148
+ @location.write(@xml_content, link_count)
149
+ @lastmod = SitemapGenerator::Utilities.current_time
150
+ @xml_content = @xml_wrapper_end = ''
141
151
  @written = true
142
152
  end
143
153
 
@@ -149,7 +159,7 @@ module SitemapGenerator
149
159
  # Reserve a name from the namer unless one has already been reserved.
150
160
  # Safe to call more than once.
151
161
  def reserve_name
152
- @reserved_name ||= @location.reserve_name
162
+ @reserved_name ||= @location.reserve_name # rubocop:disable Naming/MemoizedInstanceVariableName
153
163
  end
154
164
 
155
165
  # Return a boolean indicating whether a name has been reserved
@@ -1,17 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SitemapGenerator
2
4
  module Builder
5
+ # Builds the sitemap index XML file, listing all sitemap files.
6
+ # Manages deferred naming (waits for a second sitemap before reserving the index name)
7
+ # and controls whether an index is written at all via +create_index+.
3
8
  class SitemapIndexFile < SitemapFile
4
-
5
9
  # === Options
6
10
  #
7
11
  # * <tt>location</tt> - a SitemapGenerator::SitemapIndexLocation instance or a Hash of options
8
12
  # from which a SitemapLocation will be created for you.
9
- def initialize(opts={})
13
+ def initialize(opts = {}) # rubocop:disable Lint/MissingSuper, Metrics/MethodLength
10
14
  @location = opts.is_a?(Hash) ? SitemapGenerator::SitemapIndexLocation.new(opts) : opts
11
15
  @link_count = 0
12
16
  @sitemaps_link_count = 0
13
- @xml_content = '' # XML urlset content
14
- @xml_wrapper_start = <<-HTML
17
+ # The + prefix makes this string explicitly mutable. Ruby 2.x with
18
+ # --enable=frozen-string-literal freezes interpolated heredocs, and
19
+ # gsub! below requires a mutable string.
20
+ @xml_wrapper_start = +<<-HTML
15
21
  <?xml version="1.0" encoding="UTF-8"?>
16
22
  <sitemapindex
17
23
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -21,8 +27,9 @@ module SitemapGenerator
21
27
  >
22
28
  HTML
23
29
  @xml_wrapper_start.gsub!(/\s+/, ' ').gsub!(/ *> */, '>').strip!
24
- @xml_wrapper_end = %q[</sitemapindex>]
30
+ @xml_wrapper_end = '</sitemapindex>'
25
31
  @filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) + SitemapGenerator::Utilities.bytesize(@xml_wrapper_end)
32
+ @xml_content = @xml_wrapper_start
26
33
  @written = false
27
34
  @reserved_name = nil # holds the name reserved from the namer
28
35
  @frozen = false # rather than actually freeze, use this boolean
@@ -44,9 +51,9 @@ module SitemapGenerator
44
51
  # If a link is being added to the index manually as a string, then we
45
52
  # can assume that the index is required (unless create_index is false of course).
46
53
  # This seems like the logical thing to do.
47
- alias_method :super_add, :add
48
- def add(link, options={})
49
- if file = link.is_a?(SitemapFile) && link
54
+ alias super_add add
55
+ def add(link, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
56
+ if (file = link.is_a?(SitemapFile) && link)
50
57
  @sitemaps_link_count += file.link_count
51
58
  file.finalize! unless file.finalized?
52
59
 
@@ -57,9 +64,9 @@ module SitemapGenerator
57
64
  # first name (the index, or the sitemap). If the item is not a SitemapFile,
58
65
  # then it has been manually added and we can be sure that the user intends
59
66
  # for there to be an index.
60
- if @link_count == 0
67
+ if @link_count.zero?
61
68
  @first_sitemap = SitemapGenerator::Builder::LinkHolder.new(file, options)
62
- @link_count += 1 # pretend it's added, but don't add it yet
69
+ @link_count += 1 # pretend it's added, but don't add it yet
63
70
  else
64
71
  # need an index so make sure name is reserved and first sitemap is written out
65
72
  reserve_name unless @location.create_index == false
@@ -85,7 +92,7 @@ module SitemapGenerator
85
92
  # of <tt>bytes</tt> bytes in size. You can also pass a string and the
86
93
  # bytesize will be calculated for you.
87
94
  def file_can_fit?(bytes)
88
- bytes = bytes.is_a?(String) ? SitemapGenerator::Utilities.bytesize(bytes) : bytes
95
+ bytes = SitemapGenerator::Utilities.bytesize(bytes) if bytes.is_a?(String)
89
96
  (@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && @link_count < SitemapGenerator::MAX_SITEMAP_FILES
90
97
  end
91
98
 
@@ -94,13 +101,14 @@ module SitemapGenerator
94
101
  @sitemaps_link_count
95
102
  end
96
103
 
97
- def stats_summary(opts={})
104
+ def stats_summary(opts = {})
98
105
  str = "Sitemap stats: #{number_with_delimiter(@sitemaps_link_count)} links / #{@link_count} sitemaps"
99
- str += " / %dm%02ds" % opts[:time_taken].divmod(60) if opts[:time_taken]
106
+ str + format(' / %dm%02ds', *opts[:time_taken].divmod(60)) if opts[:time_taken] # rubocop:disable Style/FormatStringToken
100
107
  end
101
108
 
102
109
  def finalize!
103
110
  raise SitemapGenerator::SitemapFinalizedError if finalized?
111
+
104
112
  reserve_name if create_index?
105
113
  write_first_sitemap
106
114
  @frozen = true
@@ -116,7 +124,7 @@ module SitemapGenerator
116
124
  # If a link is added manually and create_index is not false, we force index
117
125
  # creation because they obviously intend for there to be an index. False otherwise.
118
126
  def create_index?
119
- @create_index || @location.create_index == true || @location.create_index == :auto && @link_count > 1
127
+ @create_index || @location.create_index == true || (@location.create_index == :auto && @link_count > 1)
120
128
  end
121
129
 
122
130
  # Return the index file URL. If create_index is true, this is the URL
@@ -135,15 +143,15 @@ module SitemapGenerator
135
143
 
136
144
  # Make sure the first sitemap has been written out and added to the index
137
145
  def write_first_sitemap
138
- if @first_sitemap
139
- @first_sitemap.link.write unless @first_sitemap.link.written?
140
- super_add(SitemapGenerator::Builder::SitemapIndexUrl.new(@first_sitemap.link, @first_sitemap.options))
141
- @link_count -= 1 # we already counted it, don't count it twice
142
- # Store the URL because if create_index is false, this is the
143
- # "index" URL
144
- @first_sitemap_url = @first_sitemap.link.location.url
145
- @first_sitemap = nil
146
- end
146
+ return unless @first_sitemap
147
+
148
+ @first_sitemap.link.write unless @first_sitemap.link.written?
149
+ super_add(SitemapGenerator::Builder::SitemapIndexUrl.new(@first_sitemap.link, @first_sitemap.options))
150
+ @link_count -= 1 # we already counted it, don't count it twice
151
+ # Store the URL because if create_index is false, this is the
152
+ # "index" URL
153
+ @first_sitemap_url = @first_sitemap.link.location.url
154
+ @first_sitemap = nil
147
155
  end
148
156
  end
149
157
  end