ftbpro_sitemap_generator 5.0.8
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 +7 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +35 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1139 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/lib/capistrano/sitemap_generator.rb +1 -0
- data/lib/capistrano/tasks/sitemap_generator.cap +36 -0
- data/lib/sitemap_generator.rb +85 -0
- data/lib/sitemap_generator/adapters.rb +0 -0
- data/lib/sitemap_generator/adapters/file_adapter.rb +43 -0
- data/lib/sitemap_generator/adapters/fog_adapter.rb +28 -0
- data/lib/sitemap_generator/adapters/s3_adapter.rb +41 -0
- data/lib/sitemap_generator/adapters/wave_adapter.rb +21 -0
- data/lib/sitemap_generator/application.rb +49 -0
- data/lib/sitemap_generator/builder.rb +8 -0
- data/lib/sitemap_generator/builder/sitemap_file.rb +172 -0
- data/lib/sitemap_generator/builder/sitemap_index_file.rb +149 -0
- data/lib/sitemap_generator/builder/sitemap_index_url.rb +28 -0
- data/lib/sitemap_generator/builder/sitemap_url.rb +250 -0
- data/lib/sitemap_generator/core_ext.rb +3 -0
- data/lib/sitemap_generator/core_ext/big_decimal.rb +45 -0
- data/lib/sitemap_generator/core_ext/numeric.rb +48 -0
- data/lib/sitemap_generator/helpers/number_helper.rb +237 -0
- data/lib/sitemap_generator/interpreter.rb +80 -0
- data/lib/sitemap_generator/link_set.rb +677 -0
- data/lib/sitemap_generator/railtie.rb +7 -0
- data/lib/sitemap_generator/sitemap_location.rb +192 -0
- data/lib/sitemap_generator/sitemap_namer.rb +75 -0
- data/lib/sitemap_generator/tasks.rb +53 -0
- data/lib/sitemap_generator/templates.rb +41 -0
- data/lib/sitemap_generator/utilities.rb +181 -0
- data/lib/tasks/sitemap_generator_tasks.rake +1 -0
- data/rails/install.rb +2 -0
- data/rails/uninstall.rb +2 -0
- data/spec/blueprint.rb +15 -0
- data/spec/files/sitemap.create.rb +12 -0
- data/spec/files/sitemap.groups.rb +49 -0
- data/spec/sitemap_generator/adapters/s3_adapter_spec.rb +23 -0
- data/spec/sitemap_generator/alternate_sitemap_spec.rb +79 -0
- data/spec/sitemap_generator/application_spec.rb +69 -0
- data/spec/sitemap_generator/builder/sitemap_file_spec.rb +110 -0
- data/spec/sitemap_generator/builder/sitemap_index_file_spec.rb +124 -0
- data/spec/sitemap_generator/builder/sitemap_index_url_spec.rb +28 -0
- data/spec/sitemap_generator/builder/sitemap_url_spec.rb +186 -0
- data/spec/sitemap_generator/core_ext/bigdecimal_spec.rb +20 -0
- data/spec/sitemap_generator/core_ext/numeric_spec.rb +43 -0
- data/spec/sitemap_generator/file_adaptor_spec.rb +20 -0
- data/spec/sitemap_generator/geo_sitemap_spec.rb +30 -0
- data/spec/sitemap_generator/helpers/number_helper_spec.rb +196 -0
- data/spec/sitemap_generator/interpreter_spec.rb +90 -0
- data/spec/sitemap_generator/link_set_spec.rb +864 -0
- data/spec/sitemap_generator/mobile_sitemap_spec.rb +27 -0
- data/spec/sitemap_generator/news_sitemap_spec.rb +42 -0
- data/spec/sitemap_generator/pagemap_sitemap_spec.rb +57 -0
- data/spec/sitemap_generator/sitemap_generator_spec.rb +582 -0
- data/spec/sitemap_generator/sitemap_groups_spec.rb +144 -0
- data/spec/sitemap_generator/sitemap_location_spec.rb +210 -0
- data/spec/sitemap_generator/sitemap_namer_spec.rb +96 -0
- data/spec/sitemap_generator/templates_spec.rb +24 -0
- data/spec/sitemap_generator/utilities/existence_spec.rb +26 -0
- data/spec/sitemap_generator/utilities/hash_spec.rb +57 -0
- data/spec/sitemap_generator/utilities/rounding_spec.rb +31 -0
- data/spec/sitemap_generator/utilities_spec.rb +101 -0
- data/spec/sitemap_generator/video_sitemap_spec.rb +117 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/file_macros.rb +39 -0
- data/spec/support/schemas/siteindex.xsd +73 -0
- data/spec/support/schemas/sitemap-geo.xsd +41 -0
- data/spec/support/schemas/sitemap-mobile.xsd +32 -0
- data/spec/support/schemas/sitemap-news.xsd +159 -0
- data/spec/support/schemas/sitemap-pagemap.xsd +97 -0
- data/spec/support/schemas/sitemap-video.xsd +643 -0
- data/spec/support/schemas/sitemap.xsd +115 -0
- data/spec/support/xml_macros.rb +67 -0
- data/templates/sitemap.rb +27 -0
- metadata +226 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
module SitemapGenerator
|
2
|
+
module Builder
|
3
|
+
class SitemapIndexFile < SitemapFile
|
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.
|
9
|
+
def initialize(opts={})
|
10
|
+
@location = opts.is_a?(Hash) ? SitemapGenerator::SitemapIndexLocation.new(opts) : opts
|
11
|
+
@link_count = 0
|
12
|
+
@sitemaps_link_count = 0
|
13
|
+
@xml_content = '' # XML urlset content
|
14
|
+
@xml_wrapper_start = <<-HTML
|
15
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
16
|
+
<sitemapindex
|
17
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
18
|
+
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
|
19
|
+
http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"
|
20
|
+
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
21
|
+
>
|
22
|
+
HTML
|
23
|
+
@xml_wrapper_start.gsub!(/\s+/, ' ').gsub!(/ *> */, '>').strip!
|
24
|
+
@xml_wrapper_end = %q[</sitemapindex>]
|
25
|
+
@filesize = SitemapGenerator::Utilities.bytesize(@xml_wrapper_start) + SitemapGenerator::Utilities.bytesize(@xml_wrapper_end)
|
26
|
+
@written = false
|
27
|
+
@reserved_name = nil # holds the name reserved from the namer
|
28
|
+
@frozen = false # rather than actually freeze, use this boolean
|
29
|
+
@first_sitemap = nil # reference to the first thing added to this index
|
30
|
+
# Store the URL of the first sitemap added because if create_index is
|
31
|
+
# false this is the "index" URL
|
32
|
+
@first_sitemap_url = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Finalize sitemaps as they are added to the index.
|
36
|
+
# If it's the first sitemap, finalize it but don't
|
37
|
+
# write it out, because we don't yet know if we need an index. If it's
|
38
|
+
# the second sitemap, we know we need an index, so reserve a name for the
|
39
|
+
# index, and go and write out the first sitemap. If it's the third or
|
40
|
+
# greater sitemap, just finalize and write it out as usual, nothing more
|
41
|
+
# needs to be done.
|
42
|
+
#
|
43
|
+
# If a link is being added to the index manually as a string, then we
|
44
|
+
# can assume that the index is required (unless create_index is false of course).
|
45
|
+
# This seems like the logical thing to do.
|
46
|
+
alias_method :super_add, :add
|
47
|
+
def add(link, options={})
|
48
|
+
if file = link.is_a?(SitemapFile) && link
|
49
|
+
@sitemaps_link_count += file.link_count
|
50
|
+
file.finalize! unless file.finalized?
|
51
|
+
|
52
|
+
# First link. If it's a SitemapFile store a reference to it and the options
|
53
|
+
# so that we can create a URL from it later. We can't create the URL yet
|
54
|
+
# because doing so fixes the sitemap file's name, and we have to wait to see
|
55
|
+
# if we have more than one link in the index before we can know who gets the
|
56
|
+
# first name (the index, or the sitemap). If the item is not a SitemapFile,
|
57
|
+
# then it has been manually added and we can be sure that the user intends
|
58
|
+
# for there to be an index.
|
59
|
+
if @link_count == 0
|
60
|
+
@first_sitemap = SitemapGenerator::Builder::LinkHolder.new(file, options)
|
61
|
+
@link_count += 1 # pretend it's added, but don't add it yet
|
62
|
+
else
|
63
|
+
# need an index so make sure name is reserved and first sitemap is written out
|
64
|
+
reserve_name unless @location.create_index == false
|
65
|
+
write_first_sitemap
|
66
|
+
file.write
|
67
|
+
super(SitemapGenerator::Builder::SitemapIndexUrl.new(file, options))
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# A link is being added manually. Obviously the user wants an index.
|
71
|
+
# This overrides the create_index setting.
|
72
|
+
unless @location.create_index == false
|
73
|
+
@create_index = true
|
74
|
+
reserve_name
|
75
|
+
end
|
76
|
+
|
77
|
+
# Use the host from the location if none provided
|
78
|
+
options[:host] ||= @location.host
|
79
|
+
super(SitemapGenerator::Builder::SitemapIndexUrl.new(link, options))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a boolean indicating whether the sitemap file can fit another link
|
84
|
+
# of <tt>bytes</tt> bytes in size. You can also pass a string and the
|
85
|
+
# bytesize will be calculated for you.
|
86
|
+
def file_can_fit?(bytes)
|
87
|
+
bytes = bytes.is_a?(String) ? SitemapGenerator::Utilities.bytesize(bytes) : bytes
|
88
|
+
(@filesize + bytes) < SitemapGenerator::MAX_SITEMAP_FILESIZE && @link_count < SitemapGenerator::MAX_SITEMAP_FILES
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return the total number of links in all sitemaps reference by this index file
|
92
|
+
def total_link_count
|
93
|
+
@sitemaps_link_count
|
94
|
+
end
|
95
|
+
|
96
|
+
def stats_summary(opts={})
|
97
|
+
str = "Sitemap stats: #{number_with_delimiter(@sitemaps_link_count)} links / #{@link_count} sitemaps"
|
98
|
+
str += " / %dm%02ds" % opts[:time_taken].divmod(60) if opts[:time_taken]
|
99
|
+
end
|
100
|
+
|
101
|
+
def finalize!
|
102
|
+
raise SitemapGenerator::SitemapFinalizedError if finalized?
|
103
|
+
reserve_name if create_index?
|
104
|
+
write_first_sitemap
|
105
|
+
@frozen = true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Write out the index if an index is needed
|
109
|
+
def write
|
110
|
+
super if create_index?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Whether or not we need to create an index file. True if create_index is true
|
114
|
+
# or if create_index is :auto and we have more than one link in the index.
|
115
|
+
# If a link is added manually and create_index is not false, we force index
|
116
|
+
# creation because they obviously intend for there to be an index. False otherwise.
|
117
|
+
def create_index?
|
118
|
+
@create_index || @location.create_index == true || @location.create_index == :auto && @link_count > 1
|
119
|
+
end
|
120
|
+
|
121
|
+
# Return the index file URL. If create_index is true, this is the URL
|
122
|
+
# of the actual index file. If create_index is false, this is the URL
|
123
|
+
# of the first sitemap that was written out. Only call this method
|
124
|
+
# *after* the files have been finalized.
|
125
|
+
def index_url
|
126
|
+
if create_index? || !@first_sitemap_url
|
127
|
+
@location.url
|
128
|
+
else
|
129
|
+
@first_sitemap_url
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
# Make sure the first sitemap has been written out and added to the index
|
136
|
+
def write_first_sitemap
|
137
|
+
if @first_sitemap
|
138
|
+
@first_sitemap.link.write unless @first_sitemap.link.written?
|
139
|
+
super_add(SitemapGenerator::Builder::SitemapIndexUrl.new(@first_sitemap.link, @first_sitemap.options))
|
140
|
+
@link_count -= 1 # we already counted it, don't count it twice
|
141
|
+
# Store the URL because if create_index is false, this is the
|
142
|
+
# "index" URL
|
143
|
+
@first_sitemap_url = @first_sitemap.link.location.url
|
144
|
+
@first_sitemap = nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module SitemapGenerator
|
4
|
+
module Builder
|
5
|
+
class SitemapIndexUrl < SitemapUrl
|
6
|
+
|
7
|
+
def initialize(path, options={})
|
8
|
+
if index = path.is_a?(SitemapGenerator::Builder::SitemapIndexFile) && path
|
9
|
+
options = SitemapGenerator::Utilities.reverse_merge(options, :host => index.location.host, :lastmod => Time.now, :changefreq => 'always', :priority => 1.0)
|
10
|
+
path = index.location.path_in_public
|
11
|
+
super(path, options)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the URL as XML
|
18
|
+
def to_xml(builder=nil)
|
19
|
+
builder = ::Builder::XmlMarkup.new if builder.nil?
|
20
|
+
builder.sitemap do
|
21
|
+
builder.loc self[:loc]
|
22
|
+
builder.lastmod w3c_date(self[:lastmod]) if self[:lastmod]
|
23
|
+
end
|
24
|
+
builder << '' # force to string
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'uri'
|
3
|
+
require 'time'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
module SitemapGenerator
|
7
|
+
module Builder
|
8
|
+
# A Hash-like class for holding information about a sitemap URL and
|
9
|
+
# generating an XML <url> element suitable for sitemaps.
|
10
|
+
class SitemapUrl < Hash
|
11
|
+
|
12
|
+
# Return a new instance with options configured on it.
|
13
|
+
#
|
14
|
+
# == Arguments
|
15
|
+
# * sitemap - a Sitemap instance, or
|
16
|
+
# * path, options - a path string and options hash
|
17
|
+
#
|
18
|
+
# == Options
|
19
|
+
# Requires a host to be set. If passing a sitemap, the sitemap must have a +default_host+
|
20
|
+
# configured. If calling with a path and options, you must include the <tt>:host</tt> option.
|
21
|
+
#
|
22
|
+
# * +host+
|
23
|
+
# * +priority+
|
24
|
+
# * +changefreq+
|
25
|
+
# * +lastmod+
|
26
|
+
# * +images+
|
27
|
+
# * +video+/+videos+
|
28
|
+
# * +geo+
|
29
|
+
# * +news+
|
30
|
+
# * +mobile+
|
31
|
+
# * +alternate+/+alternates+
|
32
|
+
# * +pagemap+
|
33
|
+
def initialize(path, options={})
|
34
|
+
options = options.dup
|
35
|
+
if sitemap = path.is_a?(SitemapGenerator::Builder::SitemapFile) && path
|
36
|
+
SitemapGenerator::Utilities.reverse_merge!(options, :host => sitemap.location.host, :lastmod => sitemap.lastmod)
|
37
|
+
path = sitemap.location.path_in_public
|
38
|
+
end
|
39
|
+
|
40
|
+
SitemapGenerator::Utilities.assert_valid_keys(options, :priority, :changefreq, :lastmod, :expires, :host, :images, :video, :geo, :news, :videos, :mobile, :alternate, :alternates, :pagemap)
|
41
|
+
SitemapGenerator::Utilities.reverse_merge!(options, :priority => 0.5, :changefreq => 'weekly', :lastmod => Time.now, :images => [], :news => {}, :videos => [], :mobile => false, :alternates => [])
|
42
|
+
raise "Cannot generate a url without a host" unless SitemapGenerator::Utilities.present?(options[:host])
|
43
|
+
|
44
|
+
if video = options.delete(:video)
|
45
|
+
options[:videos] = video.is_a?(Array) ? options[:videos].concat(video) : options[:videos] << video
|
46
|
+
end
|
47
|
+
if alternate = options.delete(:alternate)
|
48
|
+
options[:alternates] = alternate.is_a?(Array) ? options[:alternates].concat(alternate) : options[:alternates] << alternate
|
49
|
+
end
|
50
|
+
|
51
|
+
path = path.to_s.sub(/^\//, '')
|
52
|
+
loc = path.empty? ? options[:host] : (options[:host].to_s.sub(/\/$/, '') + '/' + path)
|
53
|
+
self.merge!(
|
54
|
+
:priority => options[:priority],
|
55
|
+
:changefreq => options[:changefreq],
|
56
|
+
:lastmod => options[:lastmod],
|
57
|
+
:expires => options[:expires],
|
58
|
+
:host => options[:host],
|
59
|
+
:loc => loc,
|
60
|
+
:images => prepare_images(options[:images], options[:host]),
|
61
|
+
:news => prepare_news(options[:news]),
|
62
|
+
:videos => options[:videos],
|
63
|
+
:geo => options[:geo],
|
64
|
+
:mobile => options[:mobile],
|
65
|
+
:alternates => options[:alternates],
|
66
|
+
:pagemap => options[:pagemap]
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the URL as XML
|
71
|
+
def to_xml(builder=nil)
|
72
|
+
builder = ::Builder::XmlMarkup.new if builder.nil?
|
73
|
+
builder.url do
|
74
|
+
builder.loc self[:loc]
|
75
|
+
builder.lastmod w3c_date(self[:lastmod]) if self[:lastmod]
|
76
|
+
builder.expires w3c_date(self[:expires]) if self[:expires]
|
77
|
+
builder.changefreq self[:changefreq].to_s if self[:changefreq]
|
78
|
+
builder.priority format_float(self[:priority]) if self[:priority]
|
79
|
+
|
80
|
+
unless SitemapGenerator::Utilities.blank?(self[:news])
|
81
|
+
news_data = self[:news]
|
82
|
+
builder.news:news do
|
83
|
+
builder.news:publication do
|
84
|
+
builder.news :name, news_data[:publication_name].to_s if news_data[:publication_name]
|
85
|
+
builder.news :language, news_data[:publication_language].to_s if news_data[:publication_language]
|
86
|
+
end
|
87
|
+
|
88
|
+
builder.news :access, news_data[:access].to_s if news_data[:access]
|
89
|
+
builder.news :genres, news_data[:genres].to_s if news_data[:genres]
|
90
|
+
builder.news :publication_date, w3c_date(news_data[:publication_date]) if news_data[:publication_date]
|
91
|
+
builder.news :title, news_data[:title].to_s if news_data[:title]
|
92
|
+
builder.news :keywords, news_data[:keywords].to_s if news_data[:keywords]
|
93
|
+
builder.news :stock_tickers, news_data[:stock_tickers].to_s if news_data[:stock_tickers]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
self[:images].each do |image|
|
98
|
+
builder.image:image do
|
99
|
+
builder.image :loc, image[:loc]
|
100
|
+
builder.image :caption, image[:caption].to_s if image[:caption]
|
101
|
+
builder.image :geo_location, image[:geo_location].to_s if image[:geo_location]
|
102
|
+
builder.image :title, image[:title].to_s if image[:title]
|
103
|
+
builder.image :license, image[:license].to_s if image[:license]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
self[:videos].each do |video|
|
108
|
+
builder.video :video do
|
109
|
+
builder.video :thumbnail_loc, video[:thumbnail_loc].to_s
|
110
|
+
builder.video :title, video[:title].to_s
|
111
|
+
builder.video :description, video[:description].to_s
|
112
|
+
builder.video :content_loc, video[:content_loc].to_s if video[:content_loc]
|
113
|
+
if video[:player_loc]
|
114
|
+
loc_attributes = { :allow_embed => yes_or_no_with_default(video[:allow_embed], true) }
|
115
|
+
loc_attributes[:autoplay] = video[:autoplay].to_s if SitemapGenerator::Utilities.present?(video[:autoplay])
|
116
|
+
builder.video :player_loc, video[:player_loc].to_s, loc_attributes
|
117
|
+
end
|
118
|
+
builder.video :duration, video[:duration].to_s if video[:duration]
|
119
|
+
builder.video :expiration_date, w3c_date(video[:expiration_date]) if video[:expiration_date]
|
120
|
+
builder.video :rating, format_float(video[:rating]) if video[:rating]
|
121
|
+
builder.video :view_count, video[:view_count].to_s if video[:view_count]
|
122
|
+
builder.video :publication_date, w3c_date(video[:publication_date]) if video[:publication_date]
|
123
|
+
video[:tags].each {|tag| builder.video :tag, tag.to_s } if video[:tags]
|
124
|
+
builder.video :tag, video[:tag].to_s if video[:tag]
|
125
|
+
builder.video :category, video[:category].to_s if video[:category]
|
126
|
+
builder.video :family_friendly, yes_or_no_with_default(video[:family_friendly], true) if video.has_key?(:family_friendly)
|
127
|
+
builder.video :gallery_loc, video[:gallery_loc].to_s, :title => video[:gallery_title].to_s if video[:gallery_loc]
|
128
|
+
builder.video :price, video[:price].to_s, prepare_video_price_attribs(video) if SitemapGenerator::Utilities.present?(video[:price])
|
129
|
+
if video[:uploader]
|
130
|
+
builder.video :uploader, video[:uploader].to_s, video[:uploader_info] ? { :info => video[:uploader_info].to_s } : {}
|
131
|
+
end
|
132
|
+
builder.video :live, yes_or_no_with_default(video[:live], true) if video.has_key?(:live)
|
133
|
+
builder.video :requires_subscription, yes_or_no_with_default(video[:requires_subscription], true) if video.has_key?(:requires_subscription)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
self[:alternates].each do |alternate|
|
138
|
+
rel = alternate[:nofollow] ? 'alternate nofollow' : 'alternate'
|
139
|
+
attributes = { :rel => rel, :hreflang => alternate[:lang].to_s, :href => alternate[:href].to_s }
|
140
|
+
attributes[:media] = alternate[:media].to_s if SitemapGenerator::Utilities.present?(alternate[:media])
|
141
|
+
builder.xhtml :link, attributes
|
142
|
+
end
|
143
|
+
|
144
|
+
unless SitemapGenerator::Utilities.blank?(self[:geo])
|
145
|
+
geo = self[:geo]
|
146
|
+
builder.geo :geo do
|
147
|
+
builder.geo :format, geo[:format].to_s if geo[:format]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
unless SitemapGenerator::Utilities.blank?(self[:mobile])
|
152
|
+
builder.mobile :mobile
|
153
|
+
end
|
154
|
+
|
155
|
+
unless SitemapGenerator::Utilities.blank?(self[:pagemap])
|
156
|
+
builder.pagemap :PageMap do
|
157
|
+
SitemapGenerator::Utilities.as_array(self[:pagemap][:dataobjects]).each do |dataobject|
|
158
|
+
builder.pagemap :DataObject, :type => dataobject[:type].to_s, :id => dataobject[:id].to_s do
|
159
|
+
SitemapGenerator::Utilities.as_array(dataobject[:attributes]).each do |attribute|
|
160
|
+
builder.pagemap :Attribute, attribute[:value].to_s, :name => attribute[:name].to_s
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
builder << '' # Force to string
|
168
|
+
end
|
169
|
+
|
170
|
+
def news?
|
171
|
+
SitemapGenerator::Utilities.present?(self[:news])
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
def prepare_video_price_attribs(video)
|
177
|
+
attribs = {}
|
178
|
+
attribs[:currency] = video[:price_currency].to_s # required
|
179
|
+
attribs[:type] = video[:price_type] if SitemapGenerator::Utilities.present?(video[:price_type])
|
180
|
+
attribs[:resolution] = video[:price_resolution] if SitemapGenerator::Utilities.present?(video[:price_resolution])
|
181
|
+
attribs
|
182
|
+
end
|
183
|
+
|
184
|
+
def prepare_news(news)
|
185
|
+
SitemapGenerator::Utilities.assert_valid_keys(news, :publication_name, :publication_language, :publication_date, :genres, :access, :title, :keywords, :stock_tickers) unless news.empty?
|
186
|
+
news
|
187
|
+
end
|
188
|
+
|
189
|
+
# Return an Array of image option Hashes suitable to be parsed by SitemapGenerator::Builder::SitemapFile
|
190
|
+
def prepare_images(images, host)
|
191
|
+
images.delete_if { |key,value| key[:loc] == nil }
|
192
|
+
images.each do |r|
|
193
|
+
SitemapGenerator::Utilities.assert_valid_keys(r, :loc, :caption, :geo_location, :title, :license)
|
194
|
+
r[:loc] = URI.join(host, r[:loc]).to_s
|
195
|
+
end
|
196
|
+
images[0..(SitemapGenerator::MAX_SITEMAP_IMAGES-1)]
|
197
|
+
end
|
198
|
+
|
199
|
+
def w3c_date(date)
|
200
|
+
if date.is_a?(String)
|
201
|
+
date
|
202
|
+
elsif date.respond_to?(:iso8601)
|
203
|
+
date.iso8601.sub(/Z$/i, '+00:00')
|
204
|
+
elsif date.is_a?(Date) && !date.is_a?(DateTime)
|
205
|
+
date.strftime("%Y-%m-%d")
|
206
|
+
else
|
207
|
+
zulutime = if date.is_a?(DateTime)
|
208
|
+
date.new_offset(0)
|
209
|
+
elsif date.respond_to?(:utc)
|
210
|
+
date.utc
|
211
|
+
elsif date.is_a?(Integer)
|
212
|
+
Time.at(date).utc
|
213
|
+
else
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
|
217
|
+
if zulutime
|
218
|
+
zulutime.strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
219
|
+
else
|
220
|
+
zone = date.strftime('%z').insert(-3, ':')
|
221
|
+
date.strftime("%Y-%m-%dT%H:%M:%S") + zone
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Accept a string or boolean and return 'yes' or 'no'. If a string, the
|
227
|
+
# value must be 'yes' or 'no'. Pass the default value as a boolean using `default`.
|
228
|
+
def yes_or_no(value)
|
229
|
+
if value.is_a?(String)
|
230
|
+
raise ArgumentError.new("Unrecognized value for yes/no field: #{value.inspect}") unless value =~ /^(yes|no)$/i
|
231
|
+
value.downcase
|
232
|
+
else
|
233
|
+
value ? 'yes' : 'no'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# If the value is nil, return `default` converted to either 'yes' or 'no'.
|
238
|
+
# If the value is set, return its value converted to 'yes' or 'no'.
|
239
|
+
def yes_or_no_with_default(value, default)
|
240
|
+
yes_or_no(value.nil? ? default : value)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Format a float to to one decimal precision.
|
244
|
+
# TODO: Use rounding with precision once merged with framework_agnostic.
|
245
|
+
def format_float(value)
|
246
|
+
value.is_a?(String) ? value : ('%0.1f' % value)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'psych'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
# Define our own class rather than modify the global class
|
11
|
+
class SitemapGenerator::BigDecimal < BigDecimal
|
12
|
+
YAML_TAG = 'tag:yaml.org,2002:float'
|
13
|
+
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
|
14
|
+
|
15
|
+
yaml_as YAML_TAG
|
16
|
+
|
17
|
+
# This emits the number without any scientific notation.
|
18
|
+
# This is better than self.to_f.to_s since it doesn't lose precision.
|
19
|
+
#
|
20
|
+
# Note that reconstituting YAML floats to native floats may lose precision.
|
21
|
+
def to_yaml(opts = {})
|
22
|
+
return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
|
23
|
+
|
24
|
+
YAML.quick_emit(nil, opts) do |out|
|
25
|
+
string = to_s
|
26
|
+
out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def encode_with(coder)
|
31
|
+
string = to_s
|
32
|
+
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_d
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
DEFAULT_STRING_FORMAT = 'F'
|
40
|
+
def to_formatted_s(format = DEFAULT_STRING_FORMAT)
|
41
|
+
_original_to_s(format)
|
42
|
+
end
|
43
|
+
alias_method :_original_to_s, :to_s
|
44
|
+
alias_method :to_s, :to_formatted_s
|
45
|
+
end
|