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.
- checksums.yaml +4 -4
- data/CHANGES.md +107 -61
- data/README.md +176 -171
- data/VERSION +1 -1
- data/lib/capistrano/sitemap_generator.rb +2 -0
- data/lib/sitemap_generator/adapters/active_storage_adapter.rb +31 -0
- data/lib/sitemap_generator/adapters/aws_sdk_adapter.rb +29 -13
- data/lib/sitemap_generator/adapters/file_adapter.rb +12 -13
- data/lib/sitemap_generator/adapters/fog_adapter.rb +9 -7
- data/lib/sitemap_generator/adapters/google_storage_adapter.rb +8 -5
- data/lib/sitemap_generator/adapters/s3_adapter.rb +23 -18
- data/lib/sitemap_generator/adapters/wave_adapter.rb +8 -6
- data/lib/sitemap_generator/application.rb +9 -3
- data/lib/sitemap_generator/builder/sitemap_file.rb +41 -31
- data/lib/sitemap_generator/builder/sitemap_index_file.rb +31 -23
- data/lib/sitemap_generator/builder/sitemap_index_url.rb +10 -9
- data/lib/sitemap_generator/builder/sitemap_url.rb +130 -80
- data/lib/sitemap_generator/builder.rb +7 -3
- data/lib/sitemap_generator/core_ext/big_decimal.rb +40 -33
- data/lib/sitemap_generator/core_ext/numeric.rb +52 -46
- data/lib/sitemap_generator/core_ext.rb +2 -0
- data/lib/sitemap_generator/helpers/number_helper.rb +84 -76
- data/lib/sitemap_generator/interpreter.rb +22 -18
- data/lib/sitemap_generator/link_set.rb +114 -113
- data/lib/sitemap_generator/railtie.rb +4 -1
- data/lib/sitemap_generator/simple_namer.rb +8 -6
- data/lib/sitemap_generator/sitemap_location.rb +52 -40
- data/lib/sitemap_generator/tasks.rb +16 -26
- data/lib/sitemap_generator/templates.rb +11 -9
- data/lib/sitemap_generator/utilities.rb +46 -24
- data/lib/sitemap_generator.rb +42 -27
- data/lib/tasks/sitemap_generator_tasks.rake +2 -0
- data/rails/install.rb +2 -0
- data/rails/uninstall.rb +2 -0
- data/templates/sitemap.rb +3 -1
- metadata +7 -131
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7.1.1
|
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
19
|
+
raise SitemapError, "#{dir} should be a directory!"
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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(:
|
|
26
|
+
directory = storage.directories.new(key: @fog_directory)
|
|
25
27
|
directory.files.create(
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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).
|
|
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.
|
|
29
|
+
@acl = opts.key?(:acl) ? opts.delete(:acl) : 'public'
|
|
27
30
|
@storage_options = opts
|
|
28
31
|
end
|
|
29
32
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
25
|
-
@aws_secret_access_key = opts[:aws_secret_access_key] || ENV
|
|
26
|
-
@
|
|
27
|
-
@
|
|
28
|
-
@
|
|
29
|
-
@
|
|
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
|
|
32
|
-
@fog_public = SitemapGenerator::Utilities.falsy?(fog_public)
|
|
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 = { :
|
|
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(:
|
|
56
|
+
directory = storage.directories.new(key: @fog_directory)
|
|
53
57
|
directory.files.create(
|
|
54
|
-
:
|
|
55
|
-
:
|
|
56
|
-
:
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
45
|
-
@filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) +
|
|
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
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
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
|
-
|
|
57
|
-
rescue
|
|
58
|
-
nil
|
|
63
|
+
@lastmod if location.reserved_name?
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def empty?
|
|
62
|
-
@link_count
|
|
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 =
|
|
70
|
-
(@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE &&
|
|
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 =
|
|
96
|
-
link
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
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
|
|
143
|
+
raise SitemapGenerator::SitemapError, 'Sitemap already written!' if written?
|
|
144
|
+
|
|
137
145
|
finalize! unless finalized?
|
|
138
146
|
reserve_name
|
|
139
|
-
@
|
|
140
|
-
@xml_content
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
67
|
+
if @link_count.zero?
|
|
61
68
|
@first_sitemap = SitemapGenerator::Builder::LinkHolder.new(file, options)
|
|
62
|
-
@link_count += 1
|
|
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 =
|
|
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
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|