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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +35 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1139 -0
  6. data/Rakefile +43 -0
  7. data/VERSION +1 -0
  8. data/lib/capistrano/sitemap_generator.rb +1 -0
  9. data/lib/capistrano/tasks/sitemap_generator.cap +36 -0
  10. data/lib/sitemap_generator.rb +85 -0
  11. data/lib/sitemap_generator/adapters.rb +0 -0
  12. data/lib/sitemap_generator/adapters/file_adapter.rb +43 -0
  13. data/lib/sitemap_generator/adapters/fog_adapter.rb +28 -0
  14. data/lib/sitemap_generator/adapters/s3_adapter.rb +41 -0
  15. data/lib/sitemap_generator/adapters/wave_adapter.rb +21 -0
  16. data/lib/sitemap_generator/application.rb +49 -0
  17. data/lib/sitemap_generator/builder.rb +8 -0
  18. data/lib/sitemap_generator/builder/sitemap_file.rb +172 -0
  19. data/lib/sitemap_generator/builder/sitemap_index_file.rb +149 -0
  20. data/lib/sitemap_generator/builder/sitemap_index_url.rb +28 -0
  21. data/lib/sitemap_generator/builder/sitemap_url.rb +250 -0
  22. data/lib/sitemap_generator/core_ext.rb +3 -0
  23. data/lib/sitemap_generator/core_ext/big_decimal.rb +45 -0
  24. data/lib/sitemap_generator/core_ext/numeric.rb +48 -0
  25. data/lib/sitemap_generator/helpers/number_helper.rb +237 -0
  26. data/lib/sitemap_generator/interpreter.rb +80 -0
  27. data/lib/sitemap_generator/link_set.rb +677 -0
  28. data/lib/sitemap_generator/railtie.rb +7 -0
  29. data/lib/sitemap_generator/sitemap_location.rb +192 -0
  30. data/lib/sitemap_generator/sitemap_namer.rb +75 -0
  31. data/lib/sitemap_generator/tasks.rb +53 -0
  32. data/lib/sitemap_generator/templates.rb +41 -0
  33. data/lib/sitemap_generator/utilities.rb +181 -0
  34. data/lib/tasks/sitemap_generator_tasks.rake +1 -0
  35. data/rails/install.rb +2 -0
  36. data/rails/uninstall.rb +2 -0
  37. data/spec/blueprint.rb +15 -0
  38. data/spec/files/sitemap.create.rb +12 -0
  39. data/spec/files/sitemap.groups.rb +49 -0
  40. data/spec/sitemap_generator/adapters/s3_adapter_spec.rb +23 -0
  41. data/spec/sitemap_generator/alternate_sitemap_spec.rb +79 -0
  42. data/spec/sitemap_generator/application_spec.rb +69 -0
  43. data/spec/sitemap_generator/builder/sitemap_file_spec.rb +110 -0
  44. data/spec/sitemap_generator/builder/sitemap_index_file_spec.rb +124 -0
  45. data/spec/sitemap_generator/builder/sitemap_index_url_spec.rb +28 -0
  46. data/spec/sitemap_generator/builder/sitemap_url_spec.rb +186 -0
  47. data/spec/sitemap_generator/core_ext/bigdecimal_spec.rb +20 -0
  48. data/spec/sitemap_generator/core_ext/numeric_spec.rb +43 -0
  49. data/spec/sitemap_generator/file_adaptor_spec.rb +20 -0
  50. data/spec/sitemap_generator/geo_sitemap_spec.rb +30 -0
  51. data/spec/sitemap_generator/helpers/number_helper_spec.rb +196 -0
  52. data/spec/sitemap_generator/interpreter_spec.rb +90 -0
  53. data/spec/sitemap_generator/link_set_spec.rb +864 -0
  54. data/spec/sitemap_generator/mobile_sitemap_spec.rb +27 -0
  55. data/spec/sitemap_generator/news_sitemap_spec.rb +42 -0
  56. data/spec/sitemap_generator/pagemap_sitemap_spec.rb +57 -0
  57. data/spec/sitemap_generator/sitemap_generator_spec.rb +582 -0
  58. data/spec/sitemap_generator/sitemap_groups_spec.rb +144 -0
  59. data/spec/sitemap_generator/sitemap_location_spec.rb +210 -0
  60. data/spec/sitemap_generator/sitemap_namer_spec.rb +96 -0
  61. data/spec/sitemap_generator/templates_spec.rb +24 -0
  62. data/spec/sitemap_generator/utilities/existence_spec.rb +26 -0
  63. data/spec/sitemap_generator/utilities/hash_spec.rb +57 -0
  64. data/spec/sitemap_generator/utilities/rounding_spec.rb +31 -0
  65. data/spec/sitemap_generator/utilities_spec.rb +101 -0
  66. data/spec/sitemap_generator/video_sitemap_spec.rb +117 -0
  67. data/spec/spec_helper.rb +24 -0
  68. data/spec/support/file_macros.rb +39 -0
  69. data/spec/support/schemas/siteindex.xsd +73 -0
  70. data/spec/support/schemas/sitemap-geo.xsd +41 -0
  71. data/spec/support/schemas/sitemap-mobile.xsd +32 -0
  72. data/spec/support/schemas/sitemap-news.xsd +159 -0
  73. data/spec/support/schemas/sitemap-pagemap.xsd +97 -0
  74. data/spec/support/schemas/sitemap-video.xsd +643 -0
  75. data/spec/support/schemas/sitemap.xsd +115 -0
  76. data/spec/support/xml_macros.rb +67 -0
  77. data/templates/sitemap.rb +27 -0
  78. 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,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/**/*.rb"].sort.each do |path|
2
+ require path
3
+ 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