sitemap_generator 2.2.1 → 3.0.0

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 (59) hide show
  1. data/Gemfile +9 -24
  2. data/Gemfile.lock +23 -58
  3. data/README.md +56 -75
  4. data/Rakefile +29 -117
  5. data/VERSION +1 -1
  6. data/lib/sitemap_generator.rb +24 -8
  7. data/lib/sitemap_generator/application.rb +31 -4
  8. data/lib/sitemap_generator/builder.rb +0 -6
  9. data/lib/sitemap_generator/builder/sitemap_file.rb +16 -6
  10. data/lib/sitemap_generator/builder/sitemap_index_file.rb +4 -3
  11. data/lib/sitemap_generator/builder/sitemap_index_url.rb +1 -1
  12. data/lib/sitemap_generator/builder/sitemap_url.rb +6 -8
  13. data/lib/sitemap_generator/core_ext.rb +3 -0
  14. data/lib/sitemap_generator/core_ext/big_decimal.rb +45 -0
  15. data/lib/sitemap_generator/core_ext/numeric.rb +48 -0
  16. data/lib/sitemap_generator/helpers/number_helper.rb +237 -0
  17. data/lib/sitemap_generator/interpreter.rb +1 -1
  18. data/lib/sitemap_generator/link_set.rb +39 -18
  19. data/lib/sitemap_generator/railtie.rb +2 -2
  20. data/lib/sitemap_generator/sitemap_namer.rb +1 -1
  21. data/lib/sitemap_generator/tasks.rb +53 -1
  22. data/lib/sitemap_generator/utilities.rb +107 -1
  23. data/lib/tasks/sitemap_generator_tasks.rake +1 -0
  24. data/spec/blueprint.rb +15 -0
  25. data/spec/files/sitemap.create.rb +12 -0
  26. data/spec/files/sitemap.deprecated.rb +13 -0
  27. data/spec/files/sitemap.groups.rb +37 -0
  28. data/spec/sitemap_generator/application_spec.rb +69 -0
  29. data/spec/sitemap_generator/builder/sitemap_file_spec.rb +77 -0
  30. data/spec/sitemap_generator/builder/sitemap_index_file_spec.rb +38 -0
  31. data/spec/sitemap_generator/builder/sitemap_index_url_spec.rb +16 -0
  32. data/spec/sitemap_generator/builder/sitemap_url_spec.rb +152 -0
  33. data/spec/sitemap_generator/core_ext/bigdecimal_spec.rb +20 -0
  34. data/spec/sitemap_generator/core_ext/numeric_spec.rb +43 -0
  35. data/spec/sitemap_generator/geo_sitemap_spec.rb +30 -0
  36. data/spec/sitemap_generator/helpers/number_helper_spec.rb +191 -0
  37. data/spec/sitemap_generator/interpreter_spec.rb +24 -0
  38. data/spec/sitemap_generator/link_set_spec.rb +606 -0
  39. data/spec/sitemap_generator/news_sitemap_spec.rb +42 -0
  40. data/spec/sitemap_generator/sitemap_generator_spec.rb +232 -0
  41. data/spec/sitemap_generator/sitemap_groups_spec.rb +133 -0
  42. data/spec/sitemap_generator/sitemap_location_spec.rb +124 -0
  43. data/spec/sitemap_generator/sitemap_namer_spec.rb +61 -0
  44. data/spec/sitemap_generator/templates_spec.rb +24 -0
  45. data/spec/sitemap_generator/utilities/existence_spec.rb +26 -0
  46. data/spec/sitemap_generator/utilities/hash_spec.rb +57 -0
  47. data/spec/sitemap_generator/utilities/rounding_spec.rb +31 -0
  48. data/spec/sitemap_generator/utilities_spec.rb +50 -0
  49. data/spec/sitemap_generator/video_sitemap_spec.rb +103 -0
  50. data/spec/spec_helper.rb +20 -0
  51. data/spec/support/file_macros.rb +39 -0
  52. data/spec/support/schemas/siteindex.xsd +73 -0
  53. data/spec/support/schemas/sitemap-geo.xsd +41 -0
  54. data/spec/support/schemas/sitemap-news.xsd +159 -0
  55. data/spec/support/schemas/sitemap-video.xsd +409 -0
  56. data/spec/support/schemas/sitemap.xsd +115 -0
  57. data/spec/support/xml_macros.rb +55 -0
  58. metadata +141 -122
  59. data/tasks/sitemap_generator_tasks.rake +0 -43
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.1
1
+ 3.0.0
@@ -6,24 +6,25 @@ require 'sitemap_generator/utilities'
6
6
  require 'sitemap_generator/application'
7
7
  require 'sitemap_generator/adapters'
8
8
  require 'sitemap_generator/sitemap_location'
9
- require 'active_support/core_ext/numeric'
10
9
 
11
10
  module SitemapGenerator
12
11
  autoload(:Interpreter, 'sitemap_generator/interpreter')
13
12
  autoload(:FileAdapter, 'sitemap_generator/adapters/file_adapter')
14
13
  autoload(:WaveAdapter, 'sitemap_generator/adapters/wave_adapter')
14
+ autoload(:BigDecimal, 'sitemap_generator/core_ext/big_decimal')
15
+ autoload(:Numeric, 'sitemap_generator/core_ext/numeric')
15
16
 
16
- SitemapError = Class.new(StandardError)
17
- SitemapFullError = Class.new(SitemapError)
17
+ SitemapError = Class.new(StandardError)
18
+ SitemapFullError = Class.new(SitemapError)
18
19
  SitemapFinalizedError = Class.new(SitemapError)
19
20
 
20
- silence_warnings do
21
+ Utilities.with_warnings(nil) do
21
22
  VERSION = File.read(File.dirname(__FILE__) + "/../VERSION").strip
22
23
  MAX_SITEMAP_FILES = 50_000 # max sitemap links per index file
23
24
  MAX_SITEMAP_LINKS = 50_000 # max links per sitemap
24
25
  MAX_SITEMAP_IMAGES = 1_000 # max images per url
25
26
  MAX_SITEMAP_NEWS = 1_000 # max news sitemap per index_file
26
- MAX_SITEMAP_FILESIZE = 10.megabytes # bytes
27
+ MAX_SITEMAP_FILESIZE = SitemapGenerator::Numeric.new(10).megabytes # bytes
27
28
 
28
29
  # Lazy-initialize the LinkSet instance
29
30
  Sitemap = (Class.new do
@@ -40,7 +41,22 @@ module SitemapGenerator
40
41
 
41
42
  class << self
42
43
  attr_accessor :root, :app, :templates
43
- attr_writer :yield_sitemap
44
+ attr_writer :yield_sitemap, :verbose
45
+ end
46
+
47
+ # Global default for the verbose setting.
48
+ def self.verbose
49
+ if @verbose.nil?
50
+ @verbose = if SitemapGenerator::Utilities.truthy?(ENV['VERBOSE'])
51
+ true
52
+ elsif SitemapGenerator::Utilities.falsy?(ENV['VERBOSE'])
53
+ false
54
+ else
55
+ nil
56
+ end
57
+ else
58
+ @verbose
59
+ end
44
60
  end
45
61
 
46
62
  # Returns true if we should yield the sitemap instance to the block, false otherwise.
@@ -48,9 +64,9 @@ module SitemapGenerator
48
64
  !!@yeild_sitemap
49
65
  end
50
66
 
51
- self.root = File.expand_path(File.join(File.dirname(__FILE__), '../'))
67
+ self.root = File.expand_path(File.join(File.dirname(__FILE__), '../')) # Root of the install dir, not the Rails app
52
68
  self.templates = SitemapGenerator::Templates.new(self.root)
53
- self.app = SitemapGenerator::Application.new
69
+ self.app = SitemapGenerator::Application.new
54
70
  end
55
71
 
56
72
  require 'sitemap_generator/railtie' if SitemapGenerator.app.rails3?
@@ -8,15 +8,42 @@ module SitemapGenerator
8
8
 
9
9
  # Returns a boolean indicating whether this environment is Rails 3
10
10
  #
11
- # @return [Boolean]
11
+ # @return [Boolean]
12
12
  def rails3?
13
13
  rails? && Rails.version.to_f >= 3
14
14
  rescue
15
15
  false # Rails.version defined in 2.1.0
16
16
  end
17
-
17
+
18
18
  def root
19
- Pathname.new(rails? && Rails.root || Dir.getwd)
19
+ Pathname.new(rails_root || Dir.getwd)
20
+ end
21
+
22
+ protected
23
+
24
+ # Returns the root of the Rails application,
25
+ # if this is running in a Rails context.
26
+ # Returns `nil` if no such root is defined.
27
+ #
28
+ # @return [String, nil]
29
+ def rails_root
30
+ if defined?(::Rails.root)
31
+ return ::Rails.root.to_s if ::Rails.root
32
+ raise "ERROR: Rails.root is nil!"
33
+ end
34
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
35
+ return nil
36
+ end
37
+
38
+ # Returns the environment of the Rails application,
39
+ # if this is running in a Rails context.
40
+ # Returns `nil` if no such environment is defined.
41
+ #
42
+ # @return [String, nil]
43
+ def rails_env
44
+ return ::Rails.env.to_s if defined?(::Rails.env)
45
+ return RAILS_ENV.to_s if defined?(RAILS_ENV)
46
+ return nil
20
47
  end
21
48
  end
22
- end
49
+ end
@@ -2,9 +2,3 @@ require 'sitemap_generator/builder/sitemap_file'
2
2
  require 'sitemap_generator/builder/sitemap_index_file'
3
3
  require 'sitemap_generator/builder/sitemap_url'
4
4
  require 'sitemap_generator/builder/sitemap_index_url'
5
-
6
- module SitemapGenerator
7
- module Builder
8
-
9
- end
10
- end
@@ -1,6 +1,6 @@
1
1
  require 'zlib'
2
- require 'action_view' # for number_to_human_size
3
2
  require 'fileutils'
3
+ require 'sitemap_generator/helpers/number_helper'
4
4
 
5
5
  module SitemapGenerator
6
6
  module Builder
@@ -12,8 +12,7 @@ module SitemapGenerator
12
12
  # sitemap.finalize! <- write the sitemap file and freeze the object to protect it from further modification
13
13
  #
14
14
  class SitemapFile
15
- include ActionView::Helpers::NumberHelper
16
- include ActionView::Helpers::TextHelper # Rails 2.2.2 fails with missing 'pluralize' otherwise
15
+ include SitemapGenerator::Helpers::NumberHelper
17
16
  attr_reader :link_count, :filesize, :location, :news_count
18
17
 
19
18
  # === Options
@@ -125,13 +124,24 @@ module SitemapGenerator
125
124
 
126
125
  # Return a summary string
127
126
  def summary(opts={})
128
- uncompressed_size = number_to_human_size(@filesize) rescue "#{@filesize / 8} KB"
129
- compressed_size = number_to_human_size(@location.filesize) rescue "#{@location.filesize / 8} KB"
130
- "+ #{'%-21s' % @location.path_in_public} #{'%13s' % @link_count} links / #{'%10s' % uncompressed_size} / #{'%10s' % compressed_size} gzipped"
127
+ uncompressed_size = number_to_human_size(@filesize)
128
+ compressed_size = number_to_human_size(@location.filesize)
129
+ path = ellipsis(@location.path_in_public, 47)
130
+ "+ #{'%-47s' % path} #{'%10s' % @link_count} links / #{'%10s' % compressed_size}"
131
131
  end
132
132
 
133
133
  protected
134
134
 
135
+ # Replace the last 3 characters of string with ... if the string is as big
136
+ # or bigger than max.
137
+ def ellipsis(string, max)
138
+ if string.size >= max
139
+ string[0, max - 3] + '...'
140
+ else
141
+ string
142
+ end
143
+ end
144
+
135
145
  # Return the bytesize length of the string. Ruby 1.8.6 compatible.
136
146
  def bytesize(string)
137
147
  string.respond_to?(:bytesize) ? string.bytesize : string.length
@@ -49,9 +49,10 @@ module SitemapGenerator
49
49
 
50
50
  # Return a summary string
51
51
  def summary(opts={})
52
- uncompressed_size = number_to_human_size(@filesize) rescue "#{@filesize / 8} KB"
53
- compressed_size = number_to_human_size(@location.filesize) rescue "#{@location.filesize / 8} KB"
54
- "+ #{'%-21s' % @location.path_in_public} #{'%10s' % @link_count} sitemaps / #{'%10s' % uncompressed_size} / #{'%10s' % compressed_size} gzipped"
52
+ uncompressed_size = number_to_human_size(@filesize)
53
+ compressed_size = number_to_human_size(@location.filesize)
54
+ path = ellipsis(@location.path_in_public, 44) # 47 - 3
55
+ "+ #{'%-44s' % path} #{'%10s' % @link_count} sitemaps / #{'%10s' % compressed_size}"
55
56
  end
56
57
 
57
58
  def stats_summary(opts={})
@@ -6,7 +6,7 @@ module SitemapGenerator
6
6
 
7
7
  def initialize(path, options={})
8
8
  if index = path.is_a?(SitemapGenerator::Builder::SitemapIndexFile) && path
9
- options.reverse_merge!(:host => index.location.host, :lastmod => Time.now, :changefreq => 'always', :priority => 1.0)
9
+ SitemapGenerator::Utilities.reverse_merge!(options, :host => index.location.host, :lastmod => Time.now, :changefreq => 'always', :priority => 1.0)
10
10
  path = index.location.path_in_public
11
11
  super(path, options)
12
12
  else
@@ -28,15 +28,13 @@ module SitemapGenerator
28
28
  # * +news+
29
29
  def initialize(path, options={})
30
30
  if sitemap = path.is_a?(SitemapGenerator::Builder::SitemapFile) && path
31
- options.reverse_merge!(:host => sitemap.location.host, :lastmod => sitemap.lastmod)
31
+ SitemapGenerator::Utilities.reverse_merge!(options, :host => sitemap.location.host, :lastmod => sitemap.lastmod)
32
32
  path = sitemap.location.path_in_public
33
33
  end
34
34
 
35
35
  SitemapGenerator::Utilities.assert_valid_keys(options, :priority, :changefreq, :lastmod, :host, :images, :video, :geo, :news, :videos)
36
- options.reverse_merge!(:priority => 0.5, :changefreq => 'weekly', :lastmod => Time.now, :images => [], :news => {}, :videos => [])
37
- if options[:host].blank?
38
- raise "Cannot generate a url without a host"
39
- end
36
+ SitemapGenerator::Utilities.reverse_merge!(options, :priority => 0.5, :changefreq => 'weekly', :lastmod => Time.now, :images => [], :news => {}, :videos => [])
37
+ raise "Cannot generate a url without a host" unless SitemapGenerator::Utilities.present?(options[:host])
40
38
  if video = options.delete(:video)
41
39
  options[:videos] = video.is_a?(Array) ? options[:videos].concat(video) : options[:videos] << video
42
40
  end
@@ -65,7 +63,7 @@ module SitemapGenerator
65
63
  builder.changefreq self[:changefreq] if self[:changefreq]
66
64
  builder.priority format_float(self[:priority]) if self[:priority]
67
65
 
68
- unless self[:news].blank?
66
+ unless SitemapGenerator::Utilities.blank?(self[:news])
69
67
  news_data = self[:news]
70
68
  builder.news:news do
71
69
  builder.news:publication do
@@ -117,7 +115,7 @@ module SitemapGenerator
117
115
  end
118
116
  end
119
117
 
120
- unless self[:geo].blank?
118
+ unless SitemapGenerator::Utilities.blank?(self[:geo])
121
119
  geo = self[:geo]
122
120
  builder.geo :geo do
123
121
  builder.geo :format, geo[:format] if geo[:format]
@@ -128,7 +126,7 @@ module SitemapGenerator
128
126
  end
129
127
 
130
128
  def news?
131
- self[:news].present?
129
+ SitemapGenerator::Utilities.present?(self[:news])
132
130
  end
133
131
 
134
132
  protected
@@ -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
@@ -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,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