quality_extensions 1.1.6 → 1.3.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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/License +403 -0
- data/Rakefile +2 -0
- data/Readme.rdoc +2 -3
- data/VERSION +1 -0
- data/doc_include/ReleaseNotes-0.0.10 +1 -0
- data/doc_include/ReleaseNotes-0.0.12 +1 -0
- data/doc_include/ReleaseNotes-0.0.13 +2 -0
- data/doc_include/ReleaseNotes-0.0.14 +1 -0
- data/doc_include/ReleaseNotes-0.0.15 +2 -0
- data/doc_include/ReleaseNotes-0.0.16 +1 -0
- data/doc_include/ReleaseNotes-0.0.17 +1 -0
- data/doc_include/ReleaseNotes-0.0.18 +1 -0
- data/doc_include/ReleaseNotes-0.0.19 +1 -0
- data/doc_include/ReleaseNotes-0.0.20 +1 -0
- data/doc_include/ReleaseNotes-0.0.21 +7 -0
- data/doc_include/ReleaseNotes-0.0.22 +2 -0
- data/doc_include/ReleaseNotes-0.0.23 +1 -0
- data/doc_include/ReleaseNotes-0.0.24 +3 -0
- data/doc_include/ReleaseNotes-0.0.25 +3 -0
- data/doc_include/ReleaseNotes-0.0.26 +2 -0
- data/doc_include/ReleaseNotes-0.0.27 +1 -0
- data/doc_include/ReleaseNotes-0.0.28 +1 -0
- data/doc_include/ReleaseNotes-0.0.29 +3 -0
- data/doc_include/ReleaseNotes-0.0.3 +1 -0
- data/doc_include/ReleaseNotes-0.0.30 +4 -0
- data/doc_include/ReleaseNotes-0.0.31 +1 -0
- data/doc_include/ReleaseNotes-0.0.32 +1 -0
- data/doc_include/ReleaseNotes-0.0.33 +1 -0
- data/doc_include/ReleaseNotes-0.0.34 +2 -0
- data/doc_include/ReleaseNotes-0.0.35 +1 -0
- data/doc_include/ReleaseNotes-0.0.36 +1 -0
- data/doc_include/ReleaseNotes-0.0.37 +1 -0
- data/doc_include/ReleaseNotes-0.0.38 +1 -0
- data/doc_include/ReleaseNotes-0.0.39 +1 -0
- data/doc_include/ReleaseNotes-0.0.4 +2 -0
- data/doc_include/ReleaseNotes-0.0.40 +1 -0
- data/doc_include/ReleaseNotes-0.0.41 +1 -0
- data/doc_include/ReleaseNotes-0.0.42 +2 -0
- data/doc_include/ReleaseNotes-0.0.43 +2 -0
- data/doc_include/ReleaseNotes-0.0.44 +3 -0
- data/doc_include/ReleaseNotes-0.0.45 +1 -0
- data/doc_include/ReleaseNotes-0.0.46 +1 -0
- data/doc_include/ReleaseNotes-0.0.47 +1 -0
- data/doc_include/ReleaseNotes-0.0.48 +2 -0
- data/doc_include/ReleaseNotes-0.0.49 +2 -0
- data/doc_include/ReleaseNotes-0.0.5 +1 -0
- data/doc_include/ReleaseNotes-0.0.50 +1 -0
- data/doc_include/ReleaseNotes-0.0.51 +1 -0
- data/doc_include/ReleaseNotes-0.0.52 +1 -0
- data/doc_include/ReleaseNotes-0.0.53 +3 -0
- data/doc_include/ReleaseNotes-0.0.54 +1 -0
- data/doc_include/ReleaseNotes-0.0.55 +2 -0
- data/doc_include/ReleaseNotes-0.0.56 +1 -0
- data/doc_include/ReleaseNotes-0.0.57 +3 -0
- data/doc_include/ReleaseNotes-0.0.58 +1 -0
- data/doc_include/ReleaseNotes-0.0.59 +1 -0
- data/doc_include/ReleaseNotes-0.0.6 +3 -0
- data/doc_include/ReleaseNotes-0.0.60 +3 -0
- data/doc_include/ReleaseNotes-0.0.61 +1 -0
- data/doc_include/ReleaseNotes-0.0.62 +1 -0
- data/doc_include/ReleaseNotes-0.0.63 +1 -0
- data/doc_include/ReleaseNotes-0.0.64 +1 -0
- data/doc_include/ReleaseNotes-0.0.7 +1 -0
- data/doc_include/ReleaseNotes-0.0.8 +2 -0
- data/doc_include/ReleaseNotes-0.0.9 +1 -0
- data/doc_include/ReleaseNotes-0.1.1 +1 -0
- data/doc_include/ReleaseNotes-0.1.2 +1 -0
- data/doc_include/ReleaseNotes-0.1.3 +1 -0
- data/doc_include/ReleaseNotes-0.1.4 +1 -0
- data/doc_include/ReleaseNotes-0.1.5 +0 -0
- data/doc_include/ReleaseNotes-1.0.0 +1 -0
- data/doc_include/ReleaseNotes-1.0.1 +1 -0
- data/doc_include/ReleaseNotes-1.0.2 +1 -0
- data/doc_include/ReleaseNotes-1.0.3 +1 -0
- data/doc_include/ReleaseNotes-1.1.0 +1 -0
- data/doc_include/ReleaseNotes-1.1.1 +1 -0
- data/doc_include/ReleaseNotes-1.1.6 +1 -0
- data/doc_include/assert_equal_with_difference_highlighting-there_he_is.png +0 -0
- data/doc_include/assert_equal_with_difference_highlighting-wheres_waldo.png +0 -0
- data/escape/Rakefile +62 -0
- data/escape/Readme +16 -0
- data/escape/doc_include/ReleaseNotes-0.0.1 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.3 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.4 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.5 +1 -0
- data/escape/doc_include/template/qualitysmith.rb +631 -0
- data/escape/lib/escape.rb +302 -0
- data/escape/pkg/escape-0.0.5.tgz +0 -0
- data/escape/pkg/escape-0.0.5.zip +0 -0
- data/escape/pkg/escape-0.0.5/Readme +16 -0
- data/escape/pkg/escape-0.0.5/doc_include/template/qualitysmith.rb +631 -0
- data/escape/pkg/escape-0.0.5/lib/escape.rb +302 -0
- data/lib/quality_extensions.rb +3 -1
- data/lib/quality_extensions/array/average.rb +45 -0
- data/lib/quality_extensions/array/expand_ranges.rb +2 -2
- data/lib/quality_extensions/array/shell_escape.rb +2 -2
- data/lib/quality_extensions/color/gradiate.rb +5 -5
- data/lib/quality_extensions/enumerable/every.rb +2 -2
- data/lib/quality_extensions/enumerable/grep_with_index.rb +51 -0
- data/lib/quality_extensions/enumerable/grepv.rb +1 -0
- data/lib/quality_extensions/enumerable/map_with_index.rb +2 -0
- data/lib/quality_extensions/enumerable/max_by_value.rb +72 -0
- data/lib/quality_extensions/enumerable/select_while.rb +7 -2
- data/lib/quality_extensions/enumerable/select_with_index.rb +2 -2
- data/lib/quality_extensions/float/truncate.rb +2 -1
- data/lib/quality_extensions/hash/except.rb +2 -2
- data/lib/quality_extensions/hash/to_query_string.rb +6 -6
- data/lib/quality_extensions/helpers/numbers.rb +259 -0
- data/lib/quality_extensions/kernel/die.rb +2 -2
- data/lib/quality_extensions/kernel/filter_output.rb +1 -0
- data/lib/quality_extensions/kernel/require_all.rb +7 -7
- data/lib/quality_extensions/module/guard_method.rb +3 -3
- data/lib/quality_extensions/object/non.rb +10 -4
- data/lib/quality_extensions/pathname.rb +51 -3
- data/lib/quality_extensions/regexp/named_captures.rb +7 -6
- data/lib/quality_extensions/regexp/to_plain_s.rb +116 -0
- data/lib/quality_extensions/safe_method.rb +100 -0
- data/lib/quality_extensions/safe_nil.rb +87 -11
- data/lib/quality_extensions/string/chomped_lines.rb +0 -0
- data/lib/quality_extensions/string/integer_eh.rb +2 -2
- data/lib/quality_extensions/string/prefix.rb +120 -0
- data/lib/quality_extensions/string/prefix_lines.rb +4 -69
- data/lib/quality_extensions/string/safe_numeric_conversion.rb +5 -5
- data/lib/quality_extensions/string/shell_escape.rb +3 -3
- data/lib/quality_extensions/string/to_proc.rb +119 -0
- data/lib/quality_extensions/template.rb +2 -2
- data/lib/quality_extensions/test/difference_highlighting.rb +2 -2
- data/lib/quality_extensions/version.rb +3 -0
- data/quality_extensions.gemspec +21 -0
- metadata +249 -145
- data/lib/Xfind_bug_test.rb +0 -28
|
@@ -0,0 +1 @@
|
|
|
1
|
+
str.lines.grepv(/#/) # all lines *not* beginnnig with #
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Author:: Tyler Rick
|
|
3
|
+
# Copyright:: Copyright (c) 2010, Tyler Rick
|
|
4
|
+
# License:: Ruby License
|
|
5
|
+
# Submit to Facets?::
|
|
6
|
+
# Developer notes::
|
|
7
|
+
# History::
|
|
8
|
+
#++
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module Enumerable
|
|
12
|
+
|
|
13
|
+
# Instead of returns the object in enum that gives the maximum value from the
|
|
14
|
+
# given block, like max_by does, returns the *maximum value* calculated by
|
|
15
|
+
# the given block (which is tested on each object in enum, just like in
|
|
16
|
+
# max_by).
|
|
17
|
+
#
|
|
18
|
+
# Notice the difference:
|
|
19
|
+
# ['a','abc','ab'].max_by {|el| el.length}.should == 'abc'
|
|
20
|
+
# ['a','abc','ab'].max_by_value {|el| el.length}.should == 3
|
|
21
|
+
#
|
|
22
|
+
def max_by_value(&block)
|
|
23
|
+
max_value = nil
|
|
24
|
+
each do |el|
|
|
25
|
+
value = yield el
|
|
26
|
+
if max_value.nil?
|
|
27
|
+
max_value = value
|
|
28
|
+
else
|
|
29
|
+
max_value = [value, max_value].max
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
max_value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# _____ _
|
|
43
|
+
# |_ _|__ ___| |_
|
|
44
|
+
# | |/ _ \/ __| __|
|
|
45
|
+
# | | __/\__ \ |_
|
|
46
|
+
# |_|\___||___/\__|
|
|
47
|
+
#
|
|
48
|
+
=begin test
|
|
49
|
+
require 'spec/autorun'
|
|
50
|
+
|
|
51
|
+
describe 'Enumerable#max_by_value' do
|
|
52
|
+
it 'works in simple case' do
|
|
53
|
+
[1,3,2].max_by_value {|el| el}.should == 3
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'works for array of strings' do
|
|
57
|
+
['a','abc','ab'].max_by_value {|el| el.length}.should == 3
|
|
58
|
+
['a','abc','ab'].max_by {|el| el.length}.should == 'abc'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'works in more complicated example' do
|
|
62
|
+
files = [
|
|
63
|
+
["app/controllers/application_controller.rb.orig",
|
|
64
|
+
Pathname.new('app/controllers/application_controller.rb')]
|
|
65
|
+
]
|
|
66
|
+
max = files.max_by {|a| a.first.to_s.length}.first.to_s.length
|
|
67
|
+
files.max_by_value {|a| a.first.to_s.length}.should == max
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
=end
|
|
72
|
+
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
# * !! Why does Hash#each_with_index yield |(k,v), i| but my select_with_index yields flat |k, v, i| ?
|
|
10
10
|
#++
|
|
11
11
|
|
|
12
|
-
require 'facets/kernel/
|
|
13
|
-
|
|
12
|
+
require 'facets/kernel/require_relative'
|
|
13
|
+
require_relative 'select_with_index'
|
|
14
14
|
|
|
15
15
|
module Enumerable
|
|
16
16
|
# Original version before I changed it to use select so that Hash#select_until would return a hash instead of an array.
|
|
@@ -43,12 +43,17 @@ module Enumerable
|
|
|
43
43
|
# If +inclusive+ is false, only those elements occuring _before_ the first element for which +block+ is true will be returned.
|
|
44
44
|
# If +inclusive+ is true (the default), those elements occuring up to and _including_ the first element for which +block+ is true will be returned.
|
|
45
45
|
#
|
|
46
|
+
# Examples:
|
|
47
|
+
#
|
|
46
48
|
# (0..3).select_until {|v| v == 1} # => [0, 1]
|
|
47
49
|
# (0..3).select_until(false) {|v| v == 1} # => [0]
|
|
48
50
|
#
|
|
49
51
|
# {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2} ) # => {"a"=>1, "b"=>2}
|
|
50
52
|
# {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2} ) # => {"a"=>1}
|
|
51
53
|
#
|
|
54
|
+
# puts caller # => 30 lines of context, many of them so far removed that they are irrelevant
|
|
55
|
+
# puts caller.select_until {|l| l =~ %r(/app/) } # only print the stack back to the first frame from our own code
|
|
56
|
+
#
|
|
52
57
|
def select_until(inclusive = true)
|
|
53
58
|
return self unless block_given?
|
|
54
59
|
|
|
@@ -32,11 +32,12 @@ require 'quality_extensions/module/alias_method_chain'
|
|
|
32
32
|
# end
|
|
33
33
|
|
|
34
34
|
class Float
|
|
35
|
+
# This is the same as round_at except instead of rounding (up or down) to the nearest integer, it always truncates, rounding down to the next lowest integer.
|
|
35
36
|
def truncate_with_precision(d)
|
|
36
37
|
(self * (10.0 ** d)).truncate_without_precision.to_f / (10.0 ** d)
|
|
37
38
|
end
|
|
38
39
|
alias_method_chain :truncate, :precision
|
|
39
|
-
end
|
|
40
|
+
end
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
# _____ _
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Alias for:
|
|
2
|
-
require 'facets/kernel/
|
|
3
|
-
|
|
2
|
+
require 'facets/kernel/require_relative'
|
|
3
|
+
require_relative 'only'
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
9
9
|
autoload :CGI, 'cgi'
|
|
10
10
|
require 'rubygems'
|
|
11
|
-
require 'facets/kernel/
|
|
12
|
-
|
|
11
|
+
require 'facets/kernel/require_relative'
|
|
12
|
+
require_relative '../array/to_query_string.rb'
|
|
13
13
|
|
|
14
14
|
class Hash
|
|
15
15
|
|
|
@@ -86,7 +86,7 @@ class TheTest < Test::Unit::TestCase
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
assert_equal ['foo=bar', 'names[common]=smith', 'names[uncommon][first]=lance', 'names[uncommon][last]=wilheiminkauf'].to_set,
|
|
89
|
+
assert_equal ['foo=bar', 'names[common]=smith', 'names[uncommon][first]=lance', 'names[uncommon][last]=wilheiminkauf'].to_set,
|
|
90
90
|
data.to_query_string.split(/&/).to_set
|
|
91
91
|
end
|
|
92
92
|
def test_hash_to_query_string_nesting_2
|
|
@@ -98,13 +98,13 @@ class TheTest < Test::Unit::TestCase
|
|
|
98
98
|
]
|
|
99
99
|
}
|
|
100
100
|
assert_equal 'names[common]=smith&names[uncommon][]=frankenwatzel&names[uncommon][]=wilheiminkauf', data.to_query_string('names')
|
|
101
|
-
assert_equal( {'names' => data}.to_query_string(),
|
|
101
|
+
assert_equal( {'names' => data}.to_query_string(),
|
|
102
102
|
data.to_query_string('names') )
|
|
103
103
|
end
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
def test_hash_to_query_string_encoding
|
|
106
106
|
data = {'f&r' => 'a w$'}
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
assert_equal 'f%26r=a+w%24', data.to_query_string
|
|
109
109
|
end
|
|
110
110
|
end
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Author:: Tyler Rick
|
|
3
|
+
# Copyright:: Ruby on Rails developers
|
|
4
|
+
# License:: Ruby on Rails license
|
|
5
|
+
# Submit to Facets?:: yes
|
|
6
|
+
# Developer notes::
|
|
7
|
+
# History::
|
|
8
|
+
#++
|
|
9
|
+
|
|
10
|
+
require 'facets/hash/symbolize_keys'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Array
|
|
14
|
+
def extract_options!
|
|
15
|
+
last.is_a?(::Hash) ? pop : {}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#-------------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
module Kernel
|
|
22
|
+
# TODO: use quality_extensions/helpers/numbers instead
|
|
23
|
+
# Adapted from /var/lib/gems/1.9.1/gems/actionpack-2.3.4/lib/action_view/helpers/number_helper.rb
|
|
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>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
|
|
30
|
+
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
|
|
31
|
+
#
|
|
32
|
+
# ==== Examples
|
|
33
|
+
# number_with_delimiter(12345678) # => 12,345,678
|
|
34
|
+
# number_with_delimiter(12345678.05) # => 12,345,678.05
|
|
35
|
+
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
|
|
36
|
+
# number_with_delimiter(12345678, :separator => ",") # => 12,345,678
|
|
37
|
+
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
|
|
38
|
+
# # => 98 765 432,98
|
|
39
|
+
#
|
|
40
|
+
# You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
|
|
41
|
+
# +delimiter+ as its optional second and the +separator+ as its
|
|
42
|
+
# optional third parameter:
|
|
43
|
+
# number_with_delimiter(12345678, " ") # => 12 345.678
|
|
44
|
+
# number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05
|
|
45
|
+
def number_with_delimiter(number, *args)
|
|
46
|
+
options = args.extract_options!
|
|
47
|
+
options.symbolize_keys!
|
|
48
|
+
|
|
49
|
+
unless args.empty?
|
|
50
|
+
ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
|
|
51
|
+
'instead of separate delimiter and precision arguments.', caller)
|
|
52
|
+
delimiter = args[0] || '.'
|
|
53
|
+
separator = args[1] || ','
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
delimiter ||= (options[:delimiter] || '.')
|
|
57
|
+
separator ||= (options[:separator] || ',')
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
parts = number.to_s.split('.')
|
|
61
|
+
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
|
|
62
|
+
parts.join(separator)
|
|
63
|
+
#rescue
|
|
64
|
+
# number
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
|
|
69
|
+
# You can customize the format in the +options+ hash.
|
|
70
|
+
#
|
|
71
|
+
# ==== Options
|
|
72
|
+
# * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
|
|
73
|
+
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
|
|
74
|
+
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
|
|
75
|
+
#
|
|
76
|
+
# ==== Examples
|
|
77
|
+
# number_with_precision(111.2345) # => 111.235
|
|
78
|
+
# number_with_precision(111.2345, :precision => 2) # => 111.23
|
|
79
|
+
# number_with_precision(13, :precision => 5) # => 13.00000
|
|
80
|
+
# number_with_precision(389.32314, :precision => 0) # => 389
|
|
81
|
+
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
|
|
82
|
+
# # => 1.111,23
|
|
83
|
+
#
|
|
84
|
+
# You can still use <tt>number_with_precision</tt> with the old API that accepts the
|
|
85
|
+
# +precision+ as its optional second parameter:
|
|
86
|
+
# number_with_precision(number_with_precision(111.2345, 2) # => 111.23
|
|
87
|
+
def number_with_precision(number, *args)
|
|
88
|
+
options = args.extract_options!
|
|
89
|
+
options.symbolize_keys!
|
|
90
|
+
|
|
91
|
+
precision ||= (options[:precision] || 3)
|
|
92
|
+
separator ||= (options[:separator] || '.')
|
|
93
|
+
delimiter ||= (options[:delimiter] || '')
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
|
|
97
|
+
number_with_delimiter("%01.#{precision}f" % rounded_number,
|
|
98
|
+
:separator => separator,
|
|
99
|
+
:delimiter => delimiter)
|
|
100
|
+
#rescue
|
|
101
|
+
# number
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Formats the bytes in +size+ into a more understandable representation
|
|
106
|
+
# (e.g., giving it 1500 yields 1.5 KB). This method is useful for
|
|
107
|
+
# reporting file sizes to users. This method returns nil if
|
|
108
|
+
# +size+ cannot be converted into a number. You can customize the
|
|
109
|
+
# format in the +options+ hash.
|
|
110
|
+
#
|
|
111
|
+
# ==== Options
|
|
112
|
+
# * <tt>:base</tt> - Pass in 2 (or 1024) to use binary units (KiB, MiB),
|
|
113
|
+
# pass in 10 (or 1000) to use SI (decimal) units (KB, MB)
|
|
114
|
+
# (defaults to base 10).
|
|
115
|
+
# * <tt>:precision</tt> - Sets the level of precision (defaults to 1).
|
|
116
|
+
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
|
|
117
|
+
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
|
|
118
|
+
#
|
|
119
|
+
# ==== Examples
|
|
120
|
+
# number_to_human_size(123) # => 123 Bytes
|
|
121
|
+
# number_to_human_size(1234) # => 1.2 KB
|
|
122
|
+
# number_to_human_size(12345) # => 12.3 KB
|
|
123
|
+
# number_to_human_size(1234567) # => 1.2 MB
|
|
124
|
+
# number_to_human_size(1234567890) # => 1.2 GB
|
|
125
|
+
# number_to_human_size(1234567890123) # => 1.2 TB
|
|
126
|
+
# number_to_human_size(1234567, :precision => 2) # => 1.23 MB
|
|
127
|
+
# number_to_human_size(1234567, :precision => 2, :base => 2) # => 1.18 MiB
|
|
128
|
+
# number_to_human_size(483989, :precision => 0) # => 484 KB
|
|
129
|
+
# number_to_human_size(483989, :precision => 0, :base => 2) # => 473 KiB
|
|
130
|
+
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,23 MB
|
|
131
|
+
#
|
|
132
|
+
# ==== Differences from ActiveSupport version
|
|
133
|
+
# The ActiveSupport version defaults to binary (base 2) units, while this one
|
|
134
|
+
# defaults to SI (base 10) units.
|
|
135
|
+
#
|
|
136
|
+
# The ActiveSupport incorrectly uses KB to refer to binary units, when the correct
|
|
137
|
+
# abbreviation would be KiB (see http://en.wikipedia.org/wiki/Binary_prefix).
|
|
138
|
+
#
|
|
139
|
+
# This version has a :base option to let you change the base; the ActiveSupport
|
|
140
|
+
# version does not.
|
|
141
|
+
#
|
|
142
|
+
def number_to_human_size(number, *args)
|
|
143
|
+
return nil if number.nil?
|
|
144
|
+
|
|
145
|
+
options = args.extract_options!
|
|
146
|
+
options.symbolize_keys!
|
|
147
|
+
|
|
148
|
+
precision ||= (options[:precision] || 1)
|
|
149
|
+
separator ||= (options[:separator] || '.')
|
|
150
|
+
delimiter ||= (options[:delimiter] || ',')
|
|
151
|
+
base ||= (options[:base] || 10)
|
|
152
|
+
|
|
153
|
+
# http://en.wikipedia.org/wiki/Binary_prefix
|
|
154
|
+
if base == 10 || base == 1000
|
|
155
|
+
storage_units = %w( Bytes KB MB GB TB ).freeze
|
|
156
|
+
base = 1000
|
|
157
|
+
elsif base == 2 || base == 1024
|
|
158
|
+
storage_units = %w( Bytes KiB MiB GiB TiB ).freeze
|
|
159
|
+
base = 1024
|
|
160
|
+
else
|
|
161
|
+
raise ArgumentError, "base must be 1000 or 1024"
|
|
162
|
+
end
|
|
163
|
+
storage_units_format = '%n %u'
|
|
164
|
+
|
|
165
|
+
if number.to_i < base
|
|
166
|
+
unit = number.to_i == 1 ? 'byte' : 'bytes'
|
|
167
|
+
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
|
|
168
|
+
else
|
|
169
|
+
max_exp = storage_units.size - 1
|
|
170
|
+
number = Float(number)
|
|
171
|
+
exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base 1024
|
|
172
|
+
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
|
|
173
|
+
number /= base ** exponent
|
|
174
|
+
|
|
175
|
+
unit = storage_units[exponent]
|
|
176
|
+
|
|
177
|
+
begin
|
|
178
|
+
escaped_separator = Regexp.escape(separator)
|
|
179
|
+
formatted_number = number_with_precision(number,
|
|
180
|
+
:precision => precision,
|
|
181
|
+
:separator => separator,
|
|
182
|
+
:delimiter => delimiter
|
|
183
|
+
).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
|
|
184
|
+
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
|
|
185
|
+
#rescue
|
|
186
|
+
# number
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# _____ _
|
|
194
|
+
# |_ _|__ ___| |_
|
|
195
|
+
# | |/ _ \/ __| __|
|
|
196
|
+
# | | __/\__ \ |_
|
|
197
|
+
# |_|\___||___/\__|
|
|
198
|
+
#
|
|
199
|
+
=begin test
|
|
200
|
+
require 'spec/autorun'
|
|
201
|
+
|
|
202
|
+
describe 'number_to_human_size' do
|
|
203
|
+
it 'uses decimal unit (KB) when using base 1000' do
|
|
204
|
+
number_to_human_size(524288, :base => 1000).should match(/ KB$/)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'uses the correct conversion when using base 1000' do
|
|
208
|
+
number_to_human_size(524288, :base => 1000).should match(/^524\.3 /)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'uses binary unit (KiB) when using base 1024' do
|
|
212
|
+
number_to_human_size(524288, :base => 1024).should match(/ KiB$/)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'uses the correct conversion when using base 1024' do
|
|
216
|
+
number_to_human_size(524288, :base => 1024).should match(/^512 /)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe 'number_to_human_size examples' do
|
|
221
|
+
it '1234' do
|
|
222
|
+
number_to_human_size(123).should == '123 bytes'
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it '12345' do
|
|
226
|
+
number_to_human_size(12345).should == '12.3 KB'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it '1234567' do
|
|
230
|
+
number_to_human_size(1234567).should == '1.2 MB'
|
|
231
|
+
number_to_human_size(1234567, :base => 2).should == '1.2 MiB'
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it '1234567890' do
|
|
235
|
+
number_to_human_size(1234567890).should == '1.2 GB'
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it '1234567890123' do
|
|
239
|
+
number_to_human_size(1234567890123).should == '1.2 TB'
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it '1234567' do
|
|
243
|
+
number_to_human_size(1234567, :precision => 2).should == '1.23 MB'
|
|
244
|
+
number_to_human_size(1234567, :precision => 2, :base => 2).should == '1.18 MiB'
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it '483989' do
|
|
248
|
+
number_to_human_size(483989, :precision => 0).should == '484 KB'
|
|
249
|
+
number_to_human_size(483989, :precision => 0, :base => 2).should == '473 KiB'
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it '1234567' do
|
|
253
|
+
number_to_human_size(1234567, :precision => 2, :separator => ',').should == '1,23 MB'
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
=end
|
|
259
|
+
|
|
@@ -25,8 +25,8 @@ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
|
25
25
|
require 'test/unit'
|
|
26
26
|
require 'rubygems'
|
|
27
27
|
require 'quality_extensions/kernel/capture_output'
|
|
28
|
-
#require 'facets/kernel/
|
|
29
|
-
#
|
|
28
|
+
#require 'facets/kernel/require_relative'
|
|
29
|
+
#require_relative './capture_output'
|
|
30
30
|
|
|
31
31
|
class TheTest < Test::Unit::TestCase
|
|
32
32
|
|