sitemap_generator 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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