more_ruby 0.2.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.
- checksums.yaml +7 -0
- data/LICENCE.txt +21 -0
- data/README.md +43 -0
- data/lib/more_ruby.rb +15 -0
- data/lib/more_ruby/array.rb +119 -0
- data/lib/more_ruby/enumerable.rb +11 -0
- data/lib/more_ruby/falseclass.rb +11 -0
- data/lib/more_ruby/file.rb +7 -0
- data/lib/more_ruby/fixnum.rb +10 -0
- data/lib/more_ruby/float.rb +53 -0
- data/lib/more_ruby/hash.rb +202 -0
- data/lib/more_ruby/integer.rb +20 -0
- data/lib/more_ruby/nilclass.rb +10 -0
- data/lib/more_ruby/numeric.rb +18 -0
- data/lib/more_ruby/string.rb +172 -0
- data/lib/more_ruby/time.rb +31 -0
- data/lib/more_ruby/trueclass.rb +12 -0
- data/test/test_array.rb +193 -0
- data/test/test_integer.rb +51 -0
- data/test/test_numeric.rb +27 -0
- data/test/test_string.rb +102 -0
- data/test/test_time.rb +16 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5b1771efb7dc83fea53e24b2d7b5a2f67fa7d1e2cdca9dec76ef81ad09817c30
|
4
|
+
data.tar.gz: 46a7e965a7449f3f6e0258f355e414cc2f537ad8f2c2284f06a63fddf515e26b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a8b12b7cf3c75568bfbce46433e6b07e315524ffb34d2b37bdf2f3cae73c0919e2f075f9425a88cd55638caa76e85928c04dcb02a42f1503a173dafaba15012
|
7
|
+
data.tar.gz: 3ad555c95b8f5c8512a6b24522dea8b434539584ecb237c66e56ad9154f26863a168fc16b55df38275cace7ac8e248ca9648e7587c776599ed6f0b0c2cc6aea8
|
data/LICENCE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Richard Morrisby
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
A very simple gem that adds some methods to some Ruby standard classes, e.g. <array>.include_any?, <array>.delete_random, etc.
|
2
|
+
|
3
|
+
Simply require the gem ( require "more_ruby" ) and the additional methods will be available.
|
4
|
+
|
5
|
+
# New instance methods #
|
6
|
+
|
7
|
+
## Array
|
8
|
+
:all_instance_of?, :all_kind_of?, :av, :delete_random, :include_any?, :insert_flat, :mean, :modulo_fetch, :peach, :random, :random_index, :random_insert, :random_move, :stringify_all_values_deep, :sum, :wrap_fetch
|
9
|
+
|
10
|
+
## Fixnum
|
11
|
+
:digit_count, :format_with_thousands_delimiter, :num_to_letter, :signif
|
12
|
+
|
13
|
+
## Float
|
14
|
+
:format_with_thousands_delimiter, :signif
|
15
|
+
|
16
|
+
## Hash
|
17
|
+
:all_keys, :all_values, :delete_random, :peach, :random_key, :random_pair, :random_value, :remove_empty_fields, :sort_deep, :stringify_all_values_deep, :strip_hash_of_keys, :to_a_deep, :to_xml
|
18
|
+
|
19
|
+
## Integer
|
20
|
+
:digit_count, :format_with_thousands_delimiter, :signif
|
21
|
+
|
22
|
+
## NilClass
|
23
|
+
:empty?
|
24
|
+
|
25
|
+
## Numeric
|
26
|
+
:format_with_thousands_delimiter
|
27
|
+
|
28
|
+
## String
|
29
|
+
:append, :camelcase, :camelcase_to_snakecase, :capitalize_all, :capitalize_first_letter_only, :escape, :escape_whitespace, :extract_values_from_xml_string, :formatted_number, :index_of_last_capital, :invert_case, :is_hex?, :is_integer?, :join, :pascalcase, :prefix_lines, :random_case, :snakecase, :snakecase_and_downcase, :to_bool, :unindent
|
30
|
+
|
31
|
+
## Time
|
32
|
+
:is_after?, :is_before?, :is_within?, :remove_subseconds
|
33
|
+
|
34
|
+
# New singleton methods #
|
35
|
+
|
36
|
+
## FalseClass
|
37
|
+
:maybe?, :random
|
38
|
+
|
39
|
+
## File
|
40
|
+
:basename_no_ext
|
41
|
+
|
42
|
+
## TrueClass
|
43
|
+
:maybe?, :random
|
data/lib/more_ruby.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require_relative 'more_ruby/array.rb'
|
3
|
+
require_relative 'more_ruby/enumerable.rb'
|
4
|
+
require_relative 'more_ruby/falseclass.rb'
|
5
|
+
require_relative 'more_ruby/file.rb'
|
6
|
+
require_relative 'more_ruby/fixnum.rb'
|
7
|
+
require_relative 'more_ruby/float.rb'
|
8
|
+
require_relative 'more_ruby/hash.rb'
|
9
|
+
require_relative 'more_ruby/integer.rb'
|
10
|
+
require_relative 'more_ruby/nilclass.rb'
|
11
|
+
require_relative 'more_ruby/numeric.rb'
|
12
|
+
require_relative 'more_ruby/string.rb'
|
13
|
+
require_relative 'more_ruby/time.rb'
|
14
|
+
require_relative 'more_ruby/trueclass.rb'
|
15
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
# Does the array contain any of the supplied items?
|
4
|
+
def include_any?(*items)
|
5
|
+
items.each do |i|
|
6
|
+
return true if self.include? i
|
7
|
+
end
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return a random item from the array (by index)
|
12
|
+
def random
|
13
|
+
self[rand(size)]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Deletes at a random index from the array, and returns the deleted item
|
17
|
+
def delete_random
|
18
|
+
index = rand(size)
|
19
|
+
item = self[index]
|
20
|
+
delete_at(index)
|
21
|
+
item
|
22
|
+
end
|
23
|
+
|
24
|
+
def random_index
|
25
|
+
rand(size)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Insert the supplied element to a random position within the array
|
29
|
+
def random_insert(element)
|
30
|
+
insert(rand(size), element)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Move a random element to a random position in the array
|
34
|
+
def random_move(times = 1)
|
35
|
+
times.times do
|
36
|
+
element = delete_random
|
37
|
+
random_insert(element)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the mean / average of the Numeric array. Returns a float. All entries must be Numeric.
|
42
|
+
def mean
|
43
|
+
each {|item| raise TypeError, "Cannot determine the mean of an array that contains non-Numeric objects." unless item.kind_of?(Numeric)}
|
44
|
+
inject{ |sum, item| sum + item }.to_f / size
|
45
|
+
end
|
46
|
+
alias :av :mean
|
47
|
+
|
48
|
+
# Returns the summation of the contents of the array
|
49
|
+
# Ignores nils; raises if the array contains a non-Numeric non-nil
|
50
|
+
def sum
|
51
|
+
raise TypeError, "Array contained non-numeric non-nil elements; cannot sum contents." unless compact.all_kind_of? Numeric
|
52
|
+
compact.inject(0) do |sum, item|
|
53
|
+
sum += item
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Will insert each member of the supplied array into self, such that self does not contain a new sub-array
|
58
|
+
# Insertion behaviour is more like += than << , e.g. [1, 2, 3].insert_flat(0, [4, 5]) => [4, 5, 1, 2, 3]
|
59
|
+
# Does not flatten self, so any preexisting subarrays are preserved
|
60
|
+
def insert_flat(index, o)
|
61
|
+
case o
|
62
|
+
when Array
|
63
|
+
# A -ve insertion index inserts items AFTER the item at that index, which causes problems when inserting multiple times
|
64
|
+
# Use o.each when -ve ; o.reverse_each when +ve or zero
|
65
|
+
if index < 0
|
66
|
+
o.each do |item|
|
67
|
+
insert(index, item)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
o.reverse_each do |item|
|
71
|
+
insert(index, item)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
insert(index, o)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# A simple method to fetch the item at the index. If index > size, then it 'wraps' around and looks again
|
80
|
+
# E.g. [0, 1, 2].wrap_fetch(4) returns 1
|
81
|
+
# Operation is effectively fetch(index % size)
|
82
|
+
def wrap_fetch(index)
|
83
|
+
return nil if empty?
|
84
|
+
fetch(index % size)
|
85
|
+
end
|
86
|
+
alias :modulo_fetch :wrap_fetch
|
87
|
+
|
88
|
+
# Returns true if all elements of self are kind_of?(klass)
|
89
|
+
def all_kind_of?(klass)
|
90
|
+
all? {|item| item.kind_of?(klass)}
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true if all elements of self are instance_of?(klass)
|
94
|
+
def all_instance_of?(klass)
|
95
|
+
all? {|item| item.instance_of?(klass)}
|
96
|
+
end
|
97
|
+
|
98
|
+
# A prettier way of printing an array
|
99
|
+
# Want to p each row, but have each p appear on a new line in STDOUT
|
100
|
+
def peach
|
101
|
+
self.each { |z| p z }
|
102
|
+
end
|
103
|
+
|
104
|
+
# Method to convert all values to strings, for all layers within the array
|
105
|
+
def stringify_all_values_deep
|
106
|
+
for i in 0 ... size do
|
107
|
+
v = self[i]
|
108
|
+
case v
|
109
|
+
when Array, Hash
|
110
|
+
v = v.stringify_all_values_deep
|
111
|
+
else
|
112
|
+
v = v.to_s
|
113
|
+
end
|
114
|
+
self[i] = v
|
115
|
+
end
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
class Fixnum
|
3
|
+
|
4
|
+
# Converts the number (1 <= x <= 26) to a letter
|
5
|
+
# 1 => A, 9 => I, 0 has no corresponding letter
|
6
|
+
def num_to_letter
|
7
|
+
raise "Do not call num_to_letter with a number that is not between 1 and 26" unless self >= 1 && self <= 26
|
8
|
+
(self.to_i + 64).chr
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class Float
|
4
|
+
|
5
|
+
# Two cases : self.abs >= 1, and self.abs < 1
|
6
|
+
# If >= 1, we can use .round(), adjusting for the number of digits before the decimal point
|
7
|
+
# If < 1, .round() will not ignore leading zeroes, so we have to use another means
|
8
|
+
# There is another complication in that 0.00000766.to_s returns 7.66e-06 , not 0.00000766 as we would prefer here
|
9
|
+
def signif(num_digits)
|
10
|
+
raise TypeError, "Must call #{__method__} with an Integer" unless num_digits.kind_of?(Integer)
|
11
|
+
raise ArgumentError, "Must call #{__method__} with a positive Integer" if num_digits < 0 # 0 is a special case, but allowed
|
12
|
+
# Special cases
|
13
|
+
return 0.0 if num_digits == 0
|
14
|
+
return self if self == 0.0
|
15
|
+
|
16
|
+
negative = self < 0 # remember its sign for later
|
17
|
+
|
18
|
+
# Need to check to see if the numebr is small enough that to_s will put it into scientific notation
|
19
|
+
if self.to_s =~ /^(\d)\.(\d*)e-(\d+)$/
|
20
|
+
places = 1 + $2.size + $3.to_i
|
21
|
+
s = "%.#{places}f" % self # should print out 7.66e-06 as 0.00000766
|
22
|
+
else
|
23
|
+
s = self.to_s
|
24
|
+
end
|
25
|
+
parts = s.split(".")
|
26
|
+
whole = parts[0].to_i.abs
|
27
|
+
|
28
|
+
# significant figures ignore leading zeroes, so we can only return early here if self.abs > 1
|
29
|
+
# 123.456.signif(4) should return 123.4; 123.456.signif(3) should return 123
|
30
|
+
if whole != 0 # equivalent to self.abs > 1
|
31
|
+
if num_digits <= whole.digit_count
|
32
|
+
return whole.signif(num_digits).to_f
|
33
|
+
else
|
34
|
+
return self.round(num_digits - whole.digit_count)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
fraction_as_s = parts[1] # keep as string for now
|
39
|
+
return whole.signif(num_digits).to_f unless fraction_as_s # should not be possible, but check it anyway
|
40
|
+
return whole.signif(num_digits).to_f if fraction_as_s == "0"
|
41
|
+
|
42
|
+
relevant_fraction_digits = num_digits
|
43
|
+
fraction = "0.#{fraction_as_s}".to_f
|
44
|
+
fraction_as_s =~ /^([0]*)[1-9]/
|
45
|
+
leading_zeroes_count = $1.size
|
46
|
+
rounded_fraction = fraction.round(relevant_fraction_digits + leading_zeroes_count)
|
47
|
+
if negative
|
48
|
+
return -(rounded_fraction)
|
49
|
+
else
|
50
|
+
return rounded_fraction
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
|
2
|
+
class Hash
|
3
|
+
|
4
|
+
def random_value
|
5
|
+
self.values.random
|
6
|
+
end
|
7
|
+
|
8
|
+
def random_key
|
9
|
+
self.keys.random
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a hash of the key and value
|
13
|
+
def random_pair
|
14
|
+
key = random_key
|
15
|
+
{key => self[key]}
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_random
|
19
|
+
delete(self.random_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Gathers all keys in the hash in a flat array, even if it's multi-level
|
23
|
+
# E.g. from {:a => :b, :c => {:d => :e}}, returns [:a, :c, :d]
|
24
|
+
def all_keys
|
25
|
+
list = []
|
26
|
+
self.each_pair do |k, v|
|
27
|
+
case k
|
28
|
+
when Hash
|
29
|
+
list += k.all_keys
|
30
|
+
when Array
|
31
|
+
if k[0].class == Hash # if the array contains hashes
|
32
|
+
k.each do |array_item|
|
33
|
+
list += array_item.all_keys
|
34
|
+
end
|
35
|
+
else
|
36
|
+
list << k
|
37
|
+
end
|
38
|
+
else
|
39
|
+
list << k
|
40
|
+
end # end case k
|
41
|
+
|
42
|
+
case v
|
43
|
+
when Hash
|
44
|
+
list += v.all_keys
|
45
|
+
when Array
|
46
|
+
if v[0].class == Hash # if the array contains hashes
|
47
|
+
v.each do |array_item|
|
48
|
+
list += array_item.all_keys
|
49
|
+
end
|
50
|
+
else
|
51
|
+
# do nothing
|
52
|
+
end
|
53
|
+
else
|
54
|
+
# do nothing
|
55
|
+
end # end case v
|
56
|
+
end
|
57
|
+
list
|
58
|
+
end
|
59
|
+
|
60
|
+
# Gathers all values in the hash in a flat array, even if it's multi-level
|
61
|
+
# E.g. from {:a => :b, :c => {:d => :e}}, returns [:b, :e]
|
62
|
+
def all_values
|
63
|
+
list = []
|
64
|
+
self.each_pair do |k, v|
|
65
|
+
case k
|
66
|
+
when Hash
|
67
|
+
list += k.all_values
|
68
|
+
when Array
|
69
|
+
if k[0].class == Hash # if the array contains hashes
|
70
|
+
k.each do |array_item|
|
71
|
+
list += array_item.all_values
|
72
|
+
end
|
73
|
+
else
|
74
|
+
# do nothing
|
75
|
+
end
|
76
|
+
else
|
77
|
+
# do nothing
|
78
|
+
end # end case k
|
79
|
+
|
80
|
+
case v
|
81
|
+
when Hash
|
82
|
+
list += v.all_values
|
83
|
+
when Array
|
84
|
+
if v[0].class == Hash # if the array contains hashes
|
85
|
+
v.each do |array_item|
|
86
|
+
list += array_item.all_values
|
87
|
+
end
|
88
|
+
else
|
89
|
+
list << v
|
90
|
+
end
|
91
|
+
else
|
92
|
+
list << v
|
93
|
+
end # end case v
|
94
|
+
end
|
95
|
+
list
|
96
|
+
end
|
97
|
+
|
98
|
+
# Method to convert all values to strings, for all layers within the hash
|
99
|
+
def stringify_all_values_deep
|
100
|
+
self.each_pair do |k, v|
|
101
|
+
case v
|
102
|
+
when Array, Hash
|
103
|
+
v = v.stringify_all_values_deep
|
104
|
+
else
|
105
|
+
self[k] = v.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# A prettier way of printing a hash
|
112
|
+
# Want to p each pair, but have each p appear on a new line in STDOUT
|
113
|
+
def peach
|
114
|
+
self.each_pair { |k, v| puts "#{k.inspect} => #{v.inspect}" }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Recursively deletes keys from the hash if the value of that field is nil or empty
|
118
|
+
def remove_empty_fields
|
119
|
+
self.each_pair do |k, v|
|
120
|
+
if self[k].class == Hash
|
121
|
+
self[k] = self[k].remove_empty_fields
|
122
|
+
else
|
123
|
+
self.delete(k) if v.to_s == ""
|
124
|
+
end
|
125
|
+
end
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
# Method to strip from the hash (and all sub-hashes) all keys matching the entries in the supplied array
|
130
|
+
# Entries in the array can be strings or other literals, or regexes
|
131
|
+
# Directly deletes from self; returns the modified self
|
132
|
+
def strip_hash_of_keys(array_of_keys_to_strip)
|
133
|
+
self.keys.each do |k|
|
134
|
+
strip = false
|
135
|
+
array_of_keys_to_strip.each do |i|
|
136
|
+
if i.class == Regexp
|
137
|
+
strip = true if k =~ i
|
138
|
+
else
|
139
|
+
strip = true if k == i
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if strip
|
144
|
+
self.delete(k)
|
145
|
+
else
|
146
|
+
if self[k].class == Hash
|
147
|
+
self[k] = self[k].strip_hash_of_keys(array_of_keys_to_strip)
|
148
|
+
elsif self[k].class == Array
|
149
|
+
self[k].each do |h|
|
150
|
+
h = h.strip_hash_of_keys(array_of_keys_to_strip) if h.class == Hash
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
# Method to fully convert the hash into an array, including all sub-hashes
|
159
|
+
# to_a only converts the base level
|
160
|
+
def to_a_deep
|
161
|
+
self.keys.each do |k|
|
162
|
+
if self[k].class == Hash
|
163
|
+
self[k] = self[k].to_a_deep
|
164
|
+
end
|
165
|
+
end
|
166
|
+
self.to_a
|
167
|
+
end
|
168
|
+
|
169
|
+
# Sorts the hash by its keys. This way, if two versions of the hash (mhich are identical but their k-v pairs are in different order)
|
170
|
+
# are both sorted then converted to string (to_s) they will be identical.
|
171
|
+
# Assumes that all of the keys in the hash are symbol/string/numeric and are not Hash, Array, etc.
|
172
|
+
# Assumes that each key in the hash is unique
|
173
|
+
# Sorts deep, not shallow
|
174
|
+
# Returns a new hash; does not modify self
|
175
|
+
def sort_deep
|
176
|
+
new = {}
|
177
|
+
keys = self.keys.sort
|
178
|
+
keys.each do |main_key|
|
179
|
+
self.each_pair do |k, v|
|
180
|
+
next unless k == main_key
|
181
|
+
if v.class == Hash
|
182
|
+
new[k] = v.sort_deep
|
183
|
+
else
|
184
|
+
new[k] = v
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
new
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_xml
|
192
|
+
map do |k, v|
|
193
|
+
if v.class == Hash
|
194
|
+
text = v.to_xml
|
195
|
+
else
|
196
|
+
text = v
|
197
|
+
end
|
198
|
+
"<%s>%s</%s>" % [k, text, k]
|
199
|
+
end.join
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
class Integer
|
4
|
+
|
5
|
+
# Returns the number of digits in the number
|
6
|
+
def digit_count
|
7
|
+
self.to_s.size
|
8
|
+
end
|
9
|
+
|
10
|
+
# Code taken from https://www.ruby-forum.com/topic/187036 ; all credit for mechanism goes to Harry Kakueki
|
11
|
+
# Method is same as Float#signif below; dangerous to add to common parent Numeric?
|
12
|
+
def signif(num_digits)
|
13
|
+
raise TypeError, "Must call #{__method__} with an Integer" unless num_digits.kind_of?(Integer)
|
14
|
+
raise ArgumentError, "Must call #{__method__} with a positive Integer" if num_digits < 0 # 0 is a special case, but allowed
|
15
|
+
return 0.0 if num_digits == 0
|
16
|
+
return self if self.digit_count <= num_digits
|
17
|
+
m = (num_digits - 1) - Math.log10(self.abs).floor
|
18
|
+
BigDecimal.new(((self * 10 ** m).round * 10 ** (-1 * m)).to_s).to_s('F').gsub(/\.0*$/,"").to_i
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
class NilClass
|
3
|
+
|
4
|
+
# A simple routing method in case your empty array is actually nil
|
5
|
+
# If you're calling empty?, you care if there is data in there, not if the object is actually an array
|
6
|
+
# nil is obviously empty, so return true instead of NoMethodError
|
7
|
+
def empty?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
class Numeric
|
3
|
+
|
4
|
+
# Returns a more print-friendly version of the number
|
5
|
+
# e.g. 1234567.8901 => 1,234,567.8901
|
6
|
+
# Returns a string
|
7
|
+
# Copied from http://stackoverflow.com/questions/6458990/how-to-format-a-number-1000-as-1-000
|
8
|
+
# Credit goes to user "loosecannon"
|
9
|
+
def format_with_thousands_delimiter(delimiter = ",")
|
10
|
+
parts = to_s.split(".")
|
11
|
+
if parts.size == 2
|
12
|
+
parts[0].reverse.gsub(/...(?=.)/, '\&' + delimiter).reverse + "." + parts[1]
|
13
|
+
else
|
14
|
+
to_s.reverse.gsub(/...(?=.)/, '\&' + delimiter).reverse
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
|
4
|
+
# .capitalize will upcase the first letter and lowercase everything else; this method does only the upcase part
|
5
|
+
def capitalize_first_letter_only
|
6
|
+
return self if empty?
|
7
|
+
return self[0].upcase + self[1 .. -1] # self[1 .. -1] will return "" if self is only one character long
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns new string, does not modify in place
|
11
|
+
def escape
|
12
|
+
Regexp.escape(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a new string with each character's case randomised
|
16
|
+
def random_case
|
17
|
+
new = ""
|
18
|
+
self.each_char do |x|
|
19
|
+
new << ((rand(2) % 2 == 0) ? x.upcase : x.downcase)
|
20
|
+
end
|
21
|
+
new
|
22
|
+
end
|
23
|
+
|
24
|
+
def capitalize_all
|
25
|
+
downcase.scan(/[a-z0-9]+/).map { |z| z.capitalize }.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def invert_case
|
29
|
+
s = ""
|
30
|
+
self.chars do |c|
|
31
|
+
if c =~ /[A-Z]/
|
32
|
+
s << c.downcase
|
33
|
+
elsif c =~ /[a-z]/
|
34
|
+
s << c.upcase
|
35
|
+
else
|
36
|
+
s << c
|
37
|
+
end
|
38
|
+
end
|
39
|
+
raise "invert_case failed!" unless s.size == self.size
|
40
|
+
s
|
41
|
+
end
|
42
|
+
|
43
|
+
# Prefixes each line with the supplied string
|
44
|
+
def prefix_lines(prefix)
|
45
|
+
gsub(/^/) { prefix }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Remove leading whitespace from each line in the string, using the indentation of the first line as the guide
|
49
|
+
def unindent
|
50
|
+
leading_whitespace = self[/\A\s*/]
|
51
|
+
self.gsub(/^#{leading_whitespace}/, '')
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the index of the last capital in the string (if one exists)
|
55
|
+
def index_of_last_capital
|
56
|
+
pos = 0
|
57
|
+
reverse_index = self.reverse.index /[A-Z]/
|
58
|
+
return nil if reverse_index == nil
|
59
|
+
self.size - reverse_index - 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def snakecase
|
63
|
+
gsub(/[^a-zA-Z0-9]+/, '_')
|
64
|
+
end
|
65
|
+
|
66
|
+
def snakecase_and_downcase
|
67
|
+
downcase.snakecase
|
68
|
+
end
|
69
|
+
|
70
|
+
# Like camelcase but with the first letter also capitalised
|
71
|
+
# Pascalcase is C#'s case convention
|
72
|
+
def pascalcase
|
73
|
+
downcase.scan(/[a-z0-9]+/).map { |x| x.capitalize }.join
|
74
|
+
end
|
75
|
+
|
76
|
+
def camelcase
|
77
|
+
s = self.pascalcase
|
78
|
+
s[0].downcase + s[1 .. -1]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Converts aStringInCamelCase to a_string_in_camel_case
|
82
|
+
def camelcase_to_snakecase
|
83
|
+
a = []
|
84
|
+
|
85
|
+
self =~ /(^[a-z]+)/
|
86
|
+
a << $1 if $1
|
87
|
+
|
88
|
+
m = self.scan /([A-Z]+[a-z0-9]*)/
|
89
|
+
m.flatten!
|
90
|
+
m.each do |fragment|
|
91
|
+
fragment =~ /([A-Z]+[a-z0-9]*)/
|
92
|
+
caps = $1
|
93
|
+
lower = $2
|
94
|
+
if caps.size > 1
|
95
|
+
s = "_#{caps.downcase}"
|
96
|
+
a << s
|
97
|
+
caps = "" # set to empty string so that the below caps.downcase still works
|
98
|
+
end
|
99
|
+
s = "_#{caps.downcase}#{lower}"
|
100
|
+
a << s unless s == "_"
|
101
|
+
end
|
102
|
+
a.join
|
103
|
+
end
|
104
|
+
|
105
|
+
def append(s)
|
106
|
+
self << s
|
107
|
+
end
|
108
|
+
|
109
|
+
def join(s = "")
|
110
|
+
append(s)
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_bool
|
114
|
+
return true if self.downcase == "true"
|
115
|
+
return false if self.downcase == "false"
|
116
|
+
raise "String #{self} was neither 'true' nor 'false'. Cannot convert to boolean in this custom method."
|
117
|
+
end
|
118
|
+
|
119
|
+
# Does the string look like an integer?
|
120
|
+
# !! will convrt nil to false, and anythig else (e.g. 0) to true
|
121
|
+
def is_integer?
|
122
|
+
!!(self =~ /^[0-9]+$/)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Does the string look like hex?
|
126
|
+
# !! will convrt nil to false, and anythig else (e.g. 0) to true
|
127
|
+
def is_hex?
|
128
|
+
!!(self =~ /^[0-9a-fA-F]+$/)
|
129
|
+
end
|
130
|
+
|
131
|
+
def formatted_number(separator = ",", count_limit = 3)
|
132
|
+
copy = self.dup
|
133
|
+
s = ""
|
134
|
+
count = 0
|
135
|
+
while copy.size > 0
|
136
|
+
if count == count_limit
|
137
|
+
s << separator
|
138
|
+
count = 0
|
139
|
+
else
|
140
|
+
count += 1
|
141
|
+
s << copy[-1]
|
142
|
+
copy.chop!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
s.reverse
|
146
|
+
end
|
147
|
+
|
148
|
+
# Take the XML-string and remove from it anything that looks like an XML tag
|
149
|
+
def extract_values_from_xml_string
|
150
|
+
values = []
|
151
|
+
a = self.split("<").collect { |z| z.chomp } # need to remove whitespace where possible
|
152
|
+
# Now remove from the array any items that end in a > - these items look like fragments of tags
|
153
|
+
a.delete_if { |z| z =~ />$/ }
|
154
|
+
|
155
|
+
# a might now contain nils, "" and items like "foo>bar"
|
156
|
+
a.delete_if { |z| z == "" }
|
157
|
+
a.compact!
|
158
|
+
a.each { |z| values << z.split(">")[1] }
|
159
|
+
|
160
|
+
values
|
161
|
+
end
|
162
|
+
|
163
|
+
# Within the string, whitespace chars (\n, \r, \t) are replaced by their text equivalents, e.g. \n => \\n
|
164
|
+
# Does not modify self
|
165
|
+
def escape_whitespace
|
166
|
+
copy = self.dup
|
167
|
+
copy.gsub!("\n", "\\n")
|
168
|
+
copy.gsub!("\r", "\\r")
|
169
|
+
copy.gsub!("\t", "\\t")
|
170
|
+
copy
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
class Time
|
3
|
+
|
4
|
+
# Returns a new Time object with all subseconds set to zero
|
5
|
+
def remove_subseconds
|
6
|
+
ms = self.subsec
|
7
|
+
self.clone - ms
|
8
|
+
end
|
9
|
+
|
10
|
+
# Method to compare self against other_time, returning true if self is equal to or earlier/before other_time
|
11
|
+
# This comparison is made with some leeway, so that if self is actually after other_time, but by less than
|
12
|
+
# the leeway amount (in seconds), the method will return true
|
13
|
+
def is_before?(other_time, leeway = 5)
|
14
|
+
other_time += leeway
|
15
|
+
self <= other_time
|
16
|
+
end
|
17
|
+
|
18
|
+
# Method to compare self against other_time, returning true if self is equal to or later/after other_time
|
19
|
+
# This comparison is made with some leeway, so that if self is actually before other_time, but by less than
|
20
|
+
# the leeway amount (in seconds), the method will return true
|
21
|
+
def is_after?(other_time, leeway = 5)
|
22
|
+
other_time -= leeway
|
23
|
+
self >= other_time
|
24
|
+
end
|
25
|
+
|
26
|
+
# Method to compare self against other_time, returning true if it is close enough to self in either direction
|
27
|
+
# Leeway is in seconds
|
28
|
+
def is_within?(other_time, leeway = 5)
|
29
|
+
is_before?(other_time, leeway) && is_after?(other_time, leeway)
|
30
|
+
end
|
31
|
+
end
|
data/test/test_array.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require_relative "../lib/more_ruby"
|
3
|
+
|
4
|
+
class TestArray < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_random
|
7
|
+
a = (1 .. 20).to_a
|
8
|
+
randoms = []
|
9
|
+
count = 20
|
10
|
+
count.times do
|
11
|
+
randoms << a.random
|
12
|
+
end
|
13
|
+
|
14
|
+
assert_equal(count, randoms.size)
|
15
|
+
|
16
|
+
randoms.compact!
|
17
|
+
assert_equal(count, randoms.size, ".random returned nils")
|
18
|
+
|
19
|
+
randoms.uniq!
|
20
|
+
assert_empty(randoms - a, ".random returned values not in the original array")
|
21
|
+
|
22
|
+
# Extremely unlikely to happen, but rand may roll in order for every call in this test
|
23
|
+
assert_not_equal(a, randoms, ".random did not return values in a random order")
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_delete_random_size
|
27
|
+
orig_size = 20
|
28
|
+
a = (1 .. orig_size).to_a
|
29
|
+
count = 10
|
30
|
+
count.times do |i|
|
31
|
+
deleted = a.delete_random
|
32
|
+
|
33
|
+
assert_not_equal(orig_size, a.size, ".delete_random is not deleting from self")
|
34
|
+
|
35
|
+
assert_equal(orig_size - (i + 1), a.size, ".delete_random is deleting too many from self")
|
36
|
+
|
37
|
+
# TODO would be nice to test randomness, but that's pretty difficult
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_delete_random_return
|
42
|
+
b = [3, 3, 3]
|
43
|
+
deleted = b.delete_random
|
44
|
+
|
45
|
+
assert_not_empty(b, ".delete_random is deleting by object, not by index")
|
46
|
+
assert_not_equal(b.uniq, b, ".delete_random is deleting by object, not by index")
|
47
|
+
assert_equal(deleted, b.random, ".delete_random is not returning the deleted item")
|
48
|
+
assert_not_nil(deleted, ".delete_random is not returning the deleted item")
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_mean
|
53
|
+
a = (1 .. 6).to_a # mean is 3.5
|
54
|
+
expected = 3.5
|
55
|
+
mean = a.mean
|
56
|
+
assert_kind_of(Float, mean, ".mean did not return a Float")
|
57
|
+
assert_equal(mean, expected, ".mean is not returning the correct mean")
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_av
|
61
|
+
a = (1 .. 6).to_a # mean is 3.5
|
62
|
+
expected = 3.5
|
63
|
+
mean = a.av
|
64
|
+
assert_kind_of(Float, mean, ".av did not return a Float")
|
65
|
+
assert_equal(mean, expected, ".av is not returning the correct mean")
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_mean_mixed_numerics
|
69
|
+
a = [1, 2.0, -6789, 432.123456, 0, 0x04B, 01000, 0b1000]
|
70
|
+
expected = -719.859568
|
71
|
+
mean = a.mean
|
72
|
+
assert_kind_of(Float, mean, ".mean did not return a Float")
|
73
|
+
assert_equal(mean, expected, ".mean is not returning the correct mean")
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_non_numeric_mean
|
77
|
+
assert_exception_message_correct_non_numeric_mean ["a", "b", "c"]
|
78
|
+
assert_exception_message_correct_non_numeric_mean ["1", "2", "c"]
|
79
|
+
assert_exception_message_correct_non_numeric_mean [1, 2, "c", 4]
|
80
|
+
assert_exception_message_correct_non_numeric_mean [1, 2, "3", 4]
|
81
|
+
assert_exception_message_correct_non_numeric_mean [1, nil, 3, 4]
|
82
|
+
assert_exception_message_correct_non_numeric_mean [1, false, true, 4]
|
83
|
+
end
|
84
|
+
|
85
|
+
def assert_exception_message_correct_non_numeric_mean(array)
|
86
|
+
exception = assert_raise(TypeError) {array.mean}
|
87
|
+
assert_equal("Cannot determine the mean of an array that contains non-Numeric objects.", exception.message)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_sum
|
91
|
+
a = (1 .. 6).to_a
|
92
|
+
expected = 21
|
93
|
+
sum = a.sum
|
94
|
+
assert_kind_of(Integer, sum, ".sum did not return a Integer")
|
95
|
+
assert_equal(sum, expected, ".sum is not returning the correct sum")
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_sum_floats
|
99
|
+
a = (1 .. 6).to_a
|
100
|
+
a << 4.32
|
101
|
+
expected = 25.32
|
102
|
+
sum = a.sum
|
103
|
+
assert_kind_of(Float, sum, ".sum did not return a Float")
|
104
|
+
assert_equal(sum, expected, ".sum is not returning the correct sum")
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_sum_non_numeric
|
108
|
+
a = (1 .. 6).to_a
|
109
|
+
a << "4.32"
|
110
|
+
exception = assert_raise(TypeError) {a.sum}
|
111
|
+
assert_equal("Array contained non-numeric non-nil elements; cannot sum contents.", exception.message)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_sum_with_nil
|
115
|
+
a = (1 .. 6).to_a
|
116
|
+
a << nil
|
117
|
+
a << 4.32
|
118
|
+
a << -26
|
119
|
+
expected = -0.68
|
120
|
+
sum = a.sum.signif(8) # need to work around floating-point precision issues
|
121
|
+
assert_kind_of(Float, sum, ".sum did not return a Float")
|
122
|
+
assert_equal(sum, expected, ".sum is not returning the correct sum")
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_insert_flat
|
126
|
+
a = [1, 2, 3]
|
127
|
+
b = [4, 5]
|
128
|
+
expected = [4, 5, 1, 2, 3]
|
129
|
+
a.insert_flat(0, b)
|
130
|
+
assert_equal(expected, a, ".insert_flat failed")
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_insert_flat_preserving_subarrays
|
134
|
+
a = [1, [2.1, 2.2], 3]
|
135
|
+
b = [4, 5]
|
136
|
+
expected = [1, [2.1, 2.2], 4, 5, 3]
|
137
|
+
a.insert_flat(-2, b)
|
138
|
+
assert_equal(expected.size, a.size, ".insert_flat failed to preserve preexisting subarray")
|
139
|
+
assert_equal(expected, a, ".insert_flat failed")
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_all_kind_of
|
143
|
+
a = ["A string", :smybol, false, 1]
|
144
|
+
assert_false(a.all_kind_of?(String), ".all_kind_of? returned true when there were distinctly different types in the array")
|
145
|
+
assert(a.all_kind_of?(Object), ".all_kind_of? returned true when there were distinctly different types in the array")
|
146
|
+
|
147
|
+
b = (1 .. 4).to_a
|
148
|
+
assert(b.all_kind_of?(Numeric), ".all_kind_of? returned false when the array's contents were all subclasses of the questioned class")
|
149
|
+
assert(b.all_kind_of?(Integer), ".all_kind_of? returned false when the array's contents were all subclasses of the questioned class")
|
150
|
+
assert(b.all_kind_of?(Fixnum), ".all_kind_of? returned false when the array's contents were all instances of the questioned class")
|
151
|
+
|
152
|
+
b.insert(2, 2.0)
|
153
|
+
assert(b.all_kind_of?(Numeric), ".all_kind_of? returned false when the array's contents were all subclasses of the questioned class")
|
154
|
+
assert_false(b.all_kind_of?(Integer), ".all_kind_of? returned true when there were distinctly different types in the array")
|
155
|
+
assert_false(b.all_kind_of?(Fixnum), ".all_kind_of? returned true when there were distinctly different types in the array")
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def test_all_instance_of
|
160
|
+
a = ["A string", :smybol, false, 1]
|
161
|
+
assert_false(a.all_instance_of?(String), ".all_instance_of? returned true when there were distinctly different types in the array")
|
162
|
+
assert_false(a.all_instance_of?(Object), ".all_instance_of? returned true when there were distinctly different types in the array")
|
163
|
+
|
164
|
+
b = (1 .. 4).to_a
|
165
|
+
assert_false(b.all_instance_of?(Numeric), ".all_instance_of? returned true when the array's contents were all subclasses of the questioned class")
|
166
|
+
assert_false(b.all_instance_of?(Integer), ".all_instance_of? returned true when the array's contents were all subclasses of the questioned class")
|
167
|
+
assert(b.all_instance_of?(Fixnum), ".all_instance_of? returned false when the array's contents were all instances of the questioned class")
|
168
|
+
|
169
|
+
b.insert(2, 2.0)
|
170
|
+
assert_false(b.all_instance_of?(Numeric), ".all_instance_of? returned true when the array's contents were all subclasses of the questioned class")
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_wrap_fetch
|
174
|
+
a = [:a, :b, :c, :d]
|
175
|
+
assert_equal(:a, a.wrap_fetch(0), "wrap_fetch failed")
|
176
|
+
assert_equal(:d, a.wrap_fetch(3), "wrap_fetch failed")
|
177
|
+
assert_equal(:d, a.wrap_fetch(-1), "wrap_fetch failed")
|
178
|
+
assert_equal(:a, a.wrap_fetch(-4), "wrap_fetch failed")
|
179
|
+
assert_equal(:a, a.wrap_fetch(4), "wrap_fetch failed")
|
180
|
+
assert_equal(:b, a.wrap_fetch(5), "wrap_fetch failed")
|
181
|
+
assert_equal(:c, a.wrap_fetch(494), "wrap_fetch failed")
|
182
|
+
assert_equal(:c, a.wrap_fetch(-494), "wrap_fetch failed")
|
183
|
+
|
184
|
+
b = []
|
185
|
+
assert_nil(b.wrap_fetch(0), "wrap_fetch failed")
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_modulo_fetch
|
189
|
+
a = [:a, :b, :c, :d]
|
190
|
+
assert_equal(:b, a.modulo_fetch(5), "modulo_fetch failed")
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require_relative "../lib/more_ruby"
|
3
|
+
|
4
|
+
class TestArray < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_signif_digits
|
7
|
+
a = []
|
8
|
+
a << {:raw => 2326759, :digits => 2, :pro => 2300000}
|
9
|
+
a << {:raw => 2326759, :digits => 3, :pro => 2330000}
|
10
|
+
a << {:raw => 2326759, :digits => 4, :pro => 2327000}
|
11
|
+
a << {:raw => 2326759, :digits => 5, :pro => 2326800}
|
12
|
+
a << {:raw => 2326759, :digits => 6, :pro => 2326760}
|
13
|
+
a << {:raw => 2326759, :digits => 7, :pro => 2326759}
|
14
|
+
a << {:raw => 2326759, :digits => 8, :pro => 2326759}
|
15
|
+
a << {:raw => 23, :digits => 3, :pro => 23}
|
16
|
+
a << {:raw => 23, :digits => 2, :pro => 23}
|
17
|
+
a << {:raw => 23, :digits => 1, :pro => 20}
|
18
|
+
a << {:raw => 23, :digits => 0, :pro => 0}
|
19
|
+
a << {:raw => 10.546, :digits => 0, :pro => 0}
|
20
|
+
a << {:raw => 10.546, :digits => 1, :pro => 10.0}
|
21
|
+
a << {:raw => 10.546, :digits => 2, :pro => 10.0}
|
22
|
+
a << {:raw => 10.546, :digits => 3, :pro => 10.5}
|
23
|
+
a << {:raw => 10.546, :digits => 4, :pro => 10.55}
|
24
|
+
a << {:raw => 10.546, :digits => 5, :pro => 10.546}
|
25
|
+
a << {:raw => 10.546, :digits => 6, :pro => 10.546}
|
26
|
+
a << {:raw => 0.766, :digits => 2, :pro => 0.77}
|
27
|
+
a << {:raw => 0.00000766, :digits => 4, :pro => 0.00000766}
|
28
|
+
a << {:raw => 0.00000766, :digits => 3, :pro => 0.00000766}
|
29
|
+
a << {:raw => 0.00000766, :digits => 2, :pro => 0.0000077}
|
30
|
+
a << {:raw => 0.00000766, :digits => 1, :pro => 0.000008}
|
31
|
+
a << {:raw => 0.00000766, :digits => 0, :pro => 0}
|
32
|
+
a << {:raw => 0.0, :digits => 4, :pro => 0.0}
|
33
|
+
a << {:raw => 0.0, :digits => 1, :pro => 0.0}
|
34
|
+
a << {:raw => 0.0, :digits => 0, :pro => 0.0}
|
35
|
+
a << {:raw => 1.00000766, :digits => 8, :pro => 1.0000077}
|
36
|
+
a << {:raw => 1.00000766, :digits => 3, :pro => 1.0}
|
37
|
+
a << {:raw => 1.00000766, :digits => 2, :pro => 1.0}
|
38
|
+
a << {:raw => 1.00000766, :digits => 1, :pro => 1}
|
39
|
+
a << {:raw => 1.00000766, :digits => 0, :pro => 0}
|
40
|
+
a << {:raw => -23, :digits => 3, :pro => -23}
|
41
|
+
a << {:raw => -0.0, :digits => 1, :pro => -0.0}
|
42
|
+
a << {:raw => -2326759, :digits => 5, :pro => -2326800}
|
43
|
+
a << {:raw => -1.00000766, :digits => 8, :pro => -1.0000077}
|
44
|
+
|
45
|
+
a.each do |x|
|
46
|
+
processed = x[:raw].signif(x[:digits])
|
47
|
+
assert_equal(x[:pro], processed, ".signif did not produce the expected result; for digit-count of #{x[:digits]} with #{x[:raw]}, expected #{x[:pro]} but received #{processed}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "test/unit"
|
3
|
+
require_relative "../lib/more_ruby"
|
4
|
+
|
5
|
+
class TestArray < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_format_with_thousands_delimiter
|
8
|
+
a = []
|
9
|
+
a << {:raw => 234, :expected => "234"}
|
10
|
+
a << {:raw => 2326759, :expected => "2,326,759"}
|
11
|
+
a << {:raw => 2326759, :expected => "2.326.759", :delimiter => "."}
|
12
|
+
a << {:raw => 2326759, :expected => "2.-.326.-.759", :delimiter => ".-."}
|
13
|
+
a << {:raw => 2326759.123456, :expected => "2,326,759.123456"}
|
14
|
+
a << {:raw => -2326759.123456, :expected => "-2,326,759.123456"}
|
15
|
+
a << {:raw => 0.23456, :expected => "0.23456"}
|
16
|
+
|
17
|
+
a.each do |x|
|
18
|
+
if x[:delimiter]
|
19
|
+
processed = x[:raw].format_with_thousands_delimiter(x[:delimiter])
|
20
|
+
else
|
21
|
+
processed = x[:raw].format_with_thousands_delimiter
|
22
|
+
end
|
23
|
+
assert_equal(x[:expected], processed, ".format_with_thousands_delimiter did not produce the expected result; delimiter? #{x[:delimiter] != nil ? x[:delimiter] : "<none>"} with #{x[:raw]}, expected #{x[:expected]} but received #{processed}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/test/test_string.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require_relative "../lib/more_ruby"
|
3
|
+
|
4
|
+
class TestString < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_capitalize_first_letter_only
|
7
|
+
input = "someThingELSE"
|
8
|
+
expected = "SomeThingELSE"
|
9
|
+
actual = input.capitalize_first_letter_only
|
10
|
+
assert_equal(expected, actual, ".capitalize_first_letter_only is not behaving correctly")
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_capitalize_first_letter_only_single_letter
|
14
|
+
input = "s"
|
15
|
+
expected = "S"
|
16
|
+
actual = input.capitalize_first_letter_only
|
17
|
+
assert_equal(expected, actual, ".capitalize_first_letter_only is not behaving correctly")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_capitalize_first_letter_only_empty_string
|
21
|
+
input = ""
|
22
|
+
expected = ""
|
23
|
+
actual = input.capitalize_first_letter_only
|
24
|
+
assert_equal(expected, actual, ".capitalize_first_letter_only is not behaving correctly")
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_escape_into_regex
|
28
|
+
s = "some s\\tring"
|
29
|
+
s2 = s.clone
|
30
|
+
e = s.escape
|
31
|
+
|
32
|
+
re = Regexp.new(e)
|
33
|
+
|
34
|
+
assert_equal(s, s2, ".escape modified the original string")
|
35
|
+
assert_not_equal(e, s, ".escape didn't escape parts of the string")
|
36
|
+
|
37
|
+
assert_equal(re =~ s, 0, ".escape didn't yield an escaped string that could be used to make a Regexp object")
|
38
|
+
assert_nil(re =~ e, ".escape yielded an escaped string that matched its Regexp object")
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_snakecase
|
42
|
+
a = []
|
43
|
+
a << {:raw => "thiNGS", :expected => "thiNGS"}
|
44
|
+
a << {:raw => "thiNGS-with_stuff", :expected => "thiNGS_with_stuff"}
|
45
|
+
a << {:raw => "thi--_-ngs", :expected => "thi_ngs"}
|
46
|
+
a << {:raw => "th2i<>n!!!gs", :expected => "th2i_n_gs"}
|
47
|
+
|
48
|
+
a.each do |x|
|
49
|
+
actual = x[:raw].snakecase
|
50
|
+
assert_equal(actual, x[:expected], ".snakecase didn't work properly")
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_snakecase_and_downcase
|
56
|
+
a = []
|
57
|
+
a << {:raw => "thiNGS", :expected => "things"}
|
58
|
+
a << {:raw => "thiNGS-with_stuff", :expected => "things_with_stuff"}
|
59
|
+
a << {:raw => "thi--_-ngs", :expected => "thi_ngs"}
|
60
|
+
a << {:raw => "th2i<>n!!!gs", :expected => "th2i_n_gs"}
|
61
|
+
|
62
|
+
a.each do |x|
|
63
|
+
actual = x[:raw].snakecase_and_downcase
|
64
|
+
assert_equal(actual, x[:expected], "snakecase_and_downcase didn't work properly")
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_pascalcase
|
70
|
+
a = []
|
71
|
+
a << {:raw => "thiNGS", :expected => "Things"}
|
72
|
+
a << {:raw => "thiNGS-with_stuff", :expected => "ThingsWithStuff"}
|
73
|
+
a << {:raw => "thi--_-ngs", :expected => "ThiNgs"}
|
74
|
+
a << {:raw => "th2i<>n!!!gs", :expected => "Th2iNGs"}
|
75
|
+
a << {:raw => "a_and_b", :expected => "AAndB"}
|
76
|
+
a << {:raw => "A_AND_B", :expected => "AAndB"}
|
77
|
+
a << {:raw => "AAndB", :expected => "Aandb"}
|
78
|
+
|
79
|
+
a.each do |x|
|
80
|
+
actual = x[:raw].pascalcase
|
81
|
+
assert_equal(x[:expected], actual, ".pascalcase didn't work properly")
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_camelcase
|
87
|
+
a = []
|
88
|
+
a << {:raw => "thiNGS", :expected => "things"}
|
89
|
+
a << {:raw => "thiNGS-with_stuff", :expected => "thingsWithStuff"}
|
90
|
+
a << {:raw => "thi--_-ngs", :expected => "thiNgs"}
|
91
|
+
a << {:raw => "th2i<>n!!!gs", :expected => "th2iNGs"}
|
92
|
+
a << {:raw => "a_and_b", :expected => "aAndB"}
|
93
|
+
a << {:raw => "A_AND_B", :expected => "aAndB"}
|
94
|
+
a << {:raw => "AAndB", :expected => "aandb"}
|
95
|
+
|
96
|
+
a.each do |x|
|
97
|
+
actual = x[:raw].camelcase
|
98
|
+
assert_equal( x[:expected], actual, ".camelcase didn't work properly")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
data/test/test_time.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require_relative "../lib/more_ruby"
|
3
|
+
|
4
|
+
class TestArray < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_remove_subseconds
|
7
|
+
t = Time.now
|
8
|
+
t2 = t.remove_subseconds
|
9
|
+
|
10
|
+
assert_not_equal(t, t2, "remove_subseconds didn't work")
|
11
|
+
assert_equal(t2.subsec.to_s, "0", "remove_subseconds didn't work")
|
12
|
+
assert_equal(t2.usec.to_s, "0", "remove_subseconds didn't work")
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: more_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Morrisby
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-04 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: " A very simple gem that adds some methods to some Ruby standard classes,
|
14
|
+
e.g. <array>.include_any?, <array>.delete_random, etc.\n\n Simply require the gem
|
15
|
+
( require \"more_ruby\" ) and the additional methods will be available.\n \n #
|
16
|
+
New instance methods #\n\n ## Array\n :all_instance_of?, :all_kind_of?, :av, :delete_random,
|
17
|
+
:include_any?, :insert_flat, :mean, :modulo_fetch, :peach, :random, :random_index,
|
18
|
+
:random_insert, :random_move, :stringify_all_values_deep, :sum, :wrap_fetch\n \n
|
19
|
+
\ ## Fixnum\n :digit_count, :format_with_thousands_delimiter, :num_to_letter, :signif\n
|
20
|
+
\ \n ## Float\n :format_with_thousands_delimiter, :signif\n \n ## Hash\n :all_keys,
|
21
|
+
:all_values, :delete_random, :peach, :random_key, :random_pair, :random_value, :remove_empty_fields,
|
22
|
+
:sort_deep, :stringify_all_values_deep, :strip_hash_of_keys, :to_a_deep, :to_xml\n
|
23
|
+
\ \n ## Integer\n :digit_count, :format_with_thousands_delimiter, :signif\n \n
|
24
|
+
\ ## NilClass\n :empty?\n \n ## Numeric\n :format_with_thousands_delimiter\n
|
25
|
+
\ \n ## String\n :append, :camelcase, :camelcase_to_snakecase, :capitalize_all,
|
26
|
+
:capitalize_first_letter_only, :escape, :escape_whitespace, :extract_values_from_xml_string,
|
27
|
+
:formatted_number, :index_of_last_capital, :invert_case, :is_hex?, :is_integer?,
|
28
|
+
:join, :pascalcase, :prefix_lines, :random_case, :snakecase, :snakecase_and_downcase,
|
29
|
+
:to_bool, :unindent\n \n ## Time\n :is_after?, :is_before?, :is_within?, :remove_subseconds\n
|
30
|
+
\ \n # New singleton methods #\n \n ## FalseClass\n :maybe?, :random\n \n ##
|
31
|
+
File\n :basename_no_ext\n \n ## TrueClass\n :maybe?, :random\n\n"
|
32
|
+
email: rmorrisby@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- LICENCE.txt
|
38
|
+
- README.md
|
39
|
+
- lib/more_ruby.rb
|
40
|
+
- lib/more_ruby/array.rb
|
41
|
+
- lib/more_ruby/enumerable.rb
|
42
|
+
- lib/more_ruby/falseclass.rb
|
43
|
+
- lib/more_ruby/file.rb
|
44
|
+
- lib/more_ruby/fixnum.rb
|
45
|
+
- lib/more_ruby/float.rb
|
46
|
+
- lib/more_ruby/hash.rb
|
47
|
+
- lib/more_ruby/integer.rb
|
48
|
+
- lib/more_ruby/nilclass.rb
|
49
|
+
- lib/more_ruby/numeric.rb
|
50
|
+
- lib/more_ruby/string.rb
|
51
|
+
- lib/more_ruby/time.rb
|
52
|
+
- lib/more_ruby/trueclass.rb
|
53
|
+
- test/test_array.rb
|
54
|
+
- test/test_integer.rb
|
55
|
+
- test/test_numeric.rb
|
56
|
+
- test/test_string.rb
|
57
|
+
- test/test_time.rb
|
58
|
+
homepage: https://rubygems.org/gems/more_ruby
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '1.9'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.0.3
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Adds some extra methods to some Ruby standard classes
|
81
|
+
test_files:
|
82
|
+
- test/test_array.rb
|
83
|
+
- test/test_integer.rb
|
84
|
+
- test/test_numeric.rb
|
85
|
+
- test/test_string.rb
|
86
|
+
- test/test_time.rb
|