sitemap_generator_ftbpro 5.0.4

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/adapters/file_adapter.rb +43 -0
  11. data/lib/sitemap_generator/adapters/fog_adapter.rb +28 -0
  12. data/lib/sitemap_generator/adapters/s3_adapter.rb +41 -0
  13. data/lib/sitemap_generator/adapters/wave_adapter.rb +21 -0
  14. data/lib/sitemap_generator/adapters.rb +0 -0
  15. data/lib/sitemap_generator/application.rb +49 -0
  16. data/lib/sitemap_generator/builder/sitemap_file.rb +171 -0
  17. data/lib/sitemap_generator/builder/sitemap_index_file.rb +149 -0
  18. data/lib/sitemap_generator/builder/sitemap_index_url.rb +28 -0
  19. data/lib/sitemap_generator/builder/sitemap_url.rb +250 -0
  20. data/lib/sitemap_generator/builder.rb +8 -0
  21. data/lib/sitemap_generator/core_ext/big_decimal.rb +45 -0
  22. data/lib/sitemap_generator/core_ext/numeric.rb +48 -0
  23. data/lib/sitemap_generator/core_ext.rb +3 -0
  24. data/lib/sitemap_generator/helpers/number_helper.rb +237 -0
  25. data/lib/sitemap_generator/interpreter.rb +80 -0
  26. data/lib/sitemap_generator/link_set.rb +665 -0
  27. data/lib/sitemap_generator/railtie.rb +7 -0
  28. data/lib/sitemap_generator/sitemap_location.rb +192 -0
  29. data/lib/sitemap_generator/sitemap_namer.rb +75 -0
  30. data/lib/sitemap_generator/tasks.rb +53 -0
  31. data/lib/sitemap_generator/templates.rb +41 -0
  32. data/lib/sitemap_generator/utilities.rb +181 -0
  33. data/lib/sitemap_generator.rb +82 -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,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,8 @@
1
+ require 'sitemap_generator/builder/sitemap_file'
2
+ require 'sitemap_generator/builder/sitemap_index_file'
3
+ require 'sitemap_generator/builder/sitemap_url'
4
+ require 'sitemap_generator/builder/sitemap_index_url'
5
+
6
+ module SitemapGenerator::Builder
7
+ LinkHolder = Struct.new(:link, :options)
8
+ 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
@@ -0,0 +1,48 @@
1
+ class SitemapGenerator::Numeric
2
+ KILOBYTE = 1024
3
+ MEGABYTE = KILOBYTE * 1024
4
+ GIGABYTE = MEGABYTE * 1024
5
+ TERABYTE = GIGABYTE * 1024
6
+ PETABYTE = TERABYTE * 1024
7
+ EXABYTE = PETABYTE * 1024
8
+
9
+ def initialize(number)
10
+ @number = number
11
+ end
12
+
13
+ # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
14
+ def bytes
15
+ @number
16
+ end
17
+ alias :byte :bytes
18
+
19
+ def kilobytes
20
+ @number * KILOBYTE
21
+ end
22
+ alias :kilobyte :kilobytes
23
+
24
+ def megabytes
25
+ @number * MEGABYTE
26
+ end
27
+ alias :megabyte :megabytes
28
+
29
+ def gigabytes
30
+ @number * GIGABYTE
31
+ end
32
+ alias :gigabyte :gigabytes
33
+
34
+ def terabytes
35
+ @number * TERABYTE
36
+ end
37
+ alias :terabyte :terabytes
38
+
39
+ def petabytes
40
+ @number * PETABYTE
41
+ end
42
+ alias :petabyte :petabytes
43
+
44
+ def exabytes
45
+ @number * EXABYTE
46
+ end
47
+ alias :exabyte :exabytes
48
+ 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,237 @@
1
+ # require "sitemap_generator/core_ext/big_decimal/conversions"
2
+ require "sitemap_generator/utilities"
3
+
4
+ module SitemapGenerator
5
+ # = SitemapGenerator Number Helpers
6
+ module Helpers #:nodoc:
7
+
8
+ # Provides methods for converting numbers into formatted strings.
9
+ # Methods are provided for precision, positional notation and file size
10
+ # and pretty printing.
11
+ #
12
+ # Most methods expect a +number+ argument, and will return it
13
+ # unchanged if can't be converted into a valid number.
14
+ module NumberHelper
15
+
16
+ # Raised when argument +number+ param given to the helpers is invalid and
17
+ # the option :raise is set to +true+.
18
+ class InvalidNumberError < StandardError
19
+ attr_accessor :number
20
+ def initialize(number)
21
+ @number = number
22
+ end
23
+ end
24
+
25
+ # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
26
+ # customize the format in the +options+ hash.
27
+ #
28
+ # ==== Options
29
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
30
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
31
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
32
+ #
33
+ # ==== Examples
34
+ # number_with_delimiter(12345678) # => 12,345,678
35
+ # number_with_delimiter(12345678.05) # => 12,345,678.05
36
+ # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
37
+ # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
38
+ # number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05
39
+ # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
40
+ # # => 98 765 432,98
41
+ def number_with_delimiter(number, options = {})
42
+ SitemapGenerator::Utilities.symbolize_keys!(options)
43
+
44
+ begin
45
+ Float(number)
46
+ rescue ArgumentError, TypeError
47
+ if options[:raise]
48
+ raise InvalidNumberError, number
49
+ else
50
+ return number
51
+ end
52
+ end
53
+
54
+ defaults = {
55
+ :separator => ".",
56
+ :delimiter => ",",
57
+ :precision => 3,
58
+ :significant => false,
59
+ :strip_insignificant_zeros => false
60
+ }
61
+ options = SitemapGenerator::Utilities.reverse_merge(options, defaults)
62
+
63
+ parts = number.to_s.to_str.split('.')
64
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
65
+ parts.join(options[:separator])
66
+ end
67
+
68
+ # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
69
+ # of 2 if +:significant+ is +false+, and 5 if +:significant+ is +true+).
70
+ # You can customize the format in the +options+ hash.
71
+ #
72
+ # ==== Options
73
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
74
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
75
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
76
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
77
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
78
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
79
+ #
80
+ # ==== Examples
81
+ # number_with_precision(111.2345) # => 111.235
82
+ # number_with_precision(111.2345, :precision => 2) # => 111.23
83
+ # number_with_precision(13, :precision => 5) # => 13.00000
84
+ # number_with_precision(389.32314, :precision => 0) # => 389
85
+ # number_with_precision(111.2345, :significant => true) # => 111
86
+ # number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
87
+ # number_with_precision(13, :precision => 5, :significant => true) # => 13.000
88
+ # number_with_precision(111.234, :locale => :fr) # => 111,234
89
+ # number_with_precision(13, :precision => 5, :significant => true, strip_insignificant_zeros => true)
90
+ # # => 13
91
+ # number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3
92
+ # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
93
+ # # => 1.111,23
94
+ def number_with_precision(number, options = {})
95
+ SitemapGenerator::Utilities.symbolize_keys!(options)
96
+
97
+ number = begin
98
+ Float(number)
99
+ rescue ArgumentError, TypeError
100
+ if options[:raise]
101
+ raise InvalidNumberError, number
102
+ else
103
+ return number
104
+ end
105
+ end
106
+
107
+ defaults = {
108
+ :separator => ".",
109
+ :delimiter => ",",
110
+ :precision => 3,
111
+ :significant => false,
112
+ :strip_insignificant_zeros => false
113
+ }
114
+ precision_defaults = {
115
+ :delimiter => ""
116
+ }
117
+ defaults = defaults.merge(precision_defaults)
118
+
119
+ options = SitemapGenerator::Utilities.reverse_merge(options, defaults) # Allow the user to unset default values: Eg.: :significant => false
120
+ precision = options.delete :precision
121
+ significant = options.delete :significant
122
+ strip_insignificant_zeros = options.delete :strip_insignificant_zeros
123
+
124
+ if significant and precision > 0
125
+ if number == 0
126
+ digits, rounded_number = 1, 0
127
+ else
128
+ digits = (Math.log10(number.abs) + 1).floor
129
+ rounded_number = (SitemapGenerator::BigDecimal.new(number.to_s) / SitemapGenerator::BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
130
+ digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
131
+ end
132
+ precision = precision - digits
133
+ precision = precision > 0 ? precision : 0 #don't let it be negative
134
+ else
135
+ rounded_number = SitemapGenerator::Utilities.round(SitemapGenerator::BigDecimal.new(number.to_s), precision).to_f
136
+ end
137
+ formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
138
+ if strip_insignificant_zeros
139
+ escaped_separator = Regexp.escape(options[:separator])
140
+ formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
141
+ else
142
+ formatted_number
143
+ end
144
+
145
+ end
146
+
147
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
148
+ DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
149
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
150
+
151
+ # Formats the bytes in +number+ into a more understandable representation
152
+ # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
153
+ # reporting file sizes to users. You can customize the
154
+ # format in the +options+ hash.
155
+ #
156
+ # See <tt>number_to_human</tt> if you want to pretty-print a generic number.
157
+ #
158
+ # ==== Options
159
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
160
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
161
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
162
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
163
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
164
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
165
+ # ==== Examples
166
+ # number_to_human_size(123) # => 123 Bytes
167
+ # number_to_human_size(1234) # => 1.21 KB
168
+ # number_to_human_size(12345) # => 12.1 KB
169
+ # number_to_human_size(1234567) # => 1.18 MB
170
+ # number_to_human_size(1234567890) # => 1.15 GB
171
+ # number_to_human_size(1234567890123) # => 1.12 TB
172
+ # number_to_human_size(1234567, :precision => 2) # => 1.2 MB
173
+ # number_to_human_size(483989, :precision => 2) # => 470 KB
174
+ # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB
175
+ #
176
+ # Non-significant zeros after the fractional separator are stripped out by default (set
177
+ # <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
178
+ # number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
179
+ # number_to_human_size(524288000, :precision=>5) # => "500 MB"
180
+ def number_to_human_size(number, options = {})
181
+ SitemapGenerator::Utilities.symbolize_keys!(options)
182
+
183
+ number = begin
184
+ Float(number)
185
+ rescue ArgumentError, TypeError
186
+ if options[:raise]
187
+ raise InvalidNumberError, number
188
+ else
189
+ return number
190
+ end
191
+ end
192
+
193
+ defaults = {
194
+ :separator => ".",
195
+ :delimiter => ",",
196
+ :precision => 3,
197
+ :significant => false,
198
+ :strip_insignificant_zeros => false
199
+ }
200
+ human = {
201
+ :delimiter => "",
202
+ :precision => 3,
203
+ :significant => true,
204
+ :strip_insignificant_zeros => true
205
+ }
206
+ defaults = defaults.merge(human)
207
+ options = SitemapGenerator::Utilities.reverse_merge(options, defaults)
208
+ #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
209
+ options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
210
+
211
+ storage_units_format = "%n %u"
212
+
213
+ if number.to_i < 1024
214
+ unit = number.to_i > 1 || number.to_i == 0 ? 'Bytes' : 'Byte'
215
+ storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
216
+ else
217
+ max_exp = STORAGE_UNITS.size - 1
218
+ exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
219
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
220
+ number /= 1024 ** exponent
221
+
222
+ unit_key = STORAGE_UNITS[exponent]
223
+ units = {
224
+ :byte => "Bytes",
225
+ :kb => "KB",
226
+ :mb => "MB",
227
+ :gb => "GB",
228
+ :tb => "TB"
229
+ }
230
+ unit = units[unit_key]
231
+ formatted_number = number_with_precision(number, options)
232
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,80 @@
1
+ require 'sitemap_generator'
2
+
3
+ module SitemapGenerator
4
+
5
+ # Provide a class for evaluating blocks, making the URL helpers from the framework
6
+ # and API methods available to it.
7
+ class Interpreter
8
+
9
+ if SitemapGenerator.app.rails3?
10
+ include ::Rails.application.routes.url_helpers
11
+ elsif SitemapGenerator.app.rails?
12
+ require 'action_controller'
13
+ include ActionController::UrlWriter
14
+ end
15
+
16
+ # Call with a block to evaluate a dynamic config. The only method exposed for you is
17
+ # `add` to add a link to the sitemap object attached to this interpreter.
18
+ #
19
+ # === Options
20
+ # * <tt>link_set</tt> - a LinkSet instance to use. Default is SitemapGenerator::Sitemap.
21
+ #
22
+ # All other options are passed to the LinkSet by setting them using accessor methods.
23
+ def initialize(opts={}, &block)
24
+ opts = SitemapGenerator::Utilities.reverse_merge(opts, :link_set => SitemapGenerator::Sitemap)
25
+ @linkset = opts.delete :link_set
26
+ @linkset.send(:set_options, opts)
27
+ eval(&block) if block_given?
28
+ end
29
+
30
+ def add(*args)
31
+ @linkset.add(*args)
32
+ end
33
+
34
+ def add_to_index(*args)
35
+ @linkset.add_to_index(*args)
36
+ end
37
+
38
+ # Start a new group of sitemaps. Any of the options to SitemapGenerator.new may
39
+ # be passed. Pass a block with calls to +add+ to add links to the sitemaps.
40
+ #
41
+ # All groups use the same sitemap index.
42
+ def group(*args, &block)
43
+ @linkset.group(*args, &block)
44
+ end
45
+
46
+ # Return the LinkSet instance so that you can access it from within the `create` block
47
+ # without having to use the yield_sitemap option.
48
+ def sitemap
49
+ @linkset
50
+ end
51
+
52
+ # Evaluate the block in the interpreter. Pass :yield_sitemap => true to
53
+ # yield the Interpreter instance to the block...for old-style calling.
54
+ def eval(opts={}, &block)
55
+ if block_given?
56
+ if opts[:yield_sitemap]
57
+ yield @linkset
58
+ else
59
+ instance_eval(&block)
60
+ end
61
+ end
62
+ end
63
+
64
+ # Run the interpreter on a config file using
65
+ # the default <tt>SitemapGenerator::Sitemap</tt> sitemap object.
66
+ #
67
+ # === Options
68
+ # * <tt>:config_file</tt> - full path to the config file to evaluate.
69
+ # Default is config/sitemap.rb in your application's root directory.
70
+ # All other options are passed to +new+.
71
+ def self.run(opts={}, &block)
72
+ opts = opts.dup
73
+ config_file = opts.delete(:config_file)
74
+ config_file ||= SitemapGenerator.app.root + 'config/sitemap.rb'
75
+ interpreter = self.new(opts)
76
+ interpreter.instance_eval(File.read(config_file), config_file.to_s)
77
+ interpreter
78
+ end
79
+ end
80
+ end