ruby-commons 0.1.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/LICENSE.txt +29 -0
- data/README.rdoc +3 -0
- data/lib/ruby/commons.rb +9 -0
- data/lib/ruby/commons/core_ext.rb +2 -0
- data/lib/ruby/commons/core_ext/file.rb +1 -0
- data/lib/ruby/commons/core_ext/file/extname.rb +8 -0
- data/lib/ruby/commons/core_ext/hash.rb +5 -0
- data/lib/ruby/commons/core_ext/hash/compact.rb +48 -0
- data/lib/ruby/commons/core_ext/hash/except.rb +21 -0
- data/lib/ruby/commons/core_ext/hash/keys.rb +164 -0
- data/lib/ruby/commons/core_ext/hash/slice.rb +51 -0
- data/lib/ruby/commons/core_ext/hash/values.rb +82 -0
- data/lib/ruby/commons/core_ext/json.rb +3 -0
- data/lib/ruby/commons/core_ext/json/include.rb +17 -0
- data/lib/ruby/commons/core_ext/numeric.rb +1 -0
- data/lib/ruby/commons/core_ext/numeric/integer.rb +14 -0
- data/lib/ruby/commons/core_ext/object.rb +6 -0
- data/lib/ruby/commons/core_ext/object/blank.rb +139 -0
- data/lib/ruby/commons/core_ext/object/deep_dup.rb +50 -0
- data/lib/ruby/commons/core_ext/object/duplicable.rb +100 -0
- data/lib/ruby/commons/core_ext/object/freeze.rb +26 -0
- data/lib/ruby/commons/core_ext/object/path.rb +31 -0
- data/lib/ruby/commons/core_ext/object/try.rb +102 -0
- data/lib/ruby/commons/core_ext/regexp.rb +1 -0
- data/lib/ruby/commons/core_ext/regexp/match.rb +7 -0
- data/lib/ruby/commons/core_ext/string.rb +2 -0
- data/lib/ruby/commons/core_ext/string/match.rb +7 -0
- data/lib/ruby/commons/core_ext/string/trim.rb +19 -0
- data/lib/ruby/commons/core_ext/uri.rb +3 -0
- data/lib/ruby/commons/core_ext/uri/parse.rb +11 -0
- data/lib/ruby/commons/error.rb +2 -0
- data/lib/ruby/commons/error/abstract_class_error.rb +3 -0
- data/lib/ruby/commons/gem_version.rb +17 -0
- data/lib/ruby/commons/version.rb +10 -0
- data/lib/ruby_commons.rb +1 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 160185274617ca4efe76098fa016bcfd60f96db2
|
4
|
+
data.tar.gz: 5bee257041c7f6dedf11ec6bfb5ed298eaa51e3a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15d62d5f0d8ab83f6e5fd90fcc08718decb3f4c55c75fa68b0f4f4e2708b4313638df8ce6dea0ddfd0266198b5e7ebb9663b37964e01094470b2b1b5c8f58ff7
|
7
|
+
data.tar.gz: ba1886f48f923d1e493c2b8141343ba6099aa26bcfe2a79f356fbb95bbfb15168994d32370586eba539a5e82d9720a0bb0f61aa5cb318f6a887dcc3609cf395b
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Copyright (c) 2015, MediariuM Ltd. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification,
|
4
|
+
are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice,
|
7
|
+
this list of conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
3. All advertising materials mentioning features or use of this software must
|
14
|
+
display the following acknowledgement: This product includes software
|
15
|
+
developed by the MediariuM Ltd.
|
16
|
+
|
17
|
+
4. Neither the name of MediariuM Ltd. nor the names of its contributors may
|
18
|
+
be used to endorse or promote products derived from this software without
|
19
|
+
specific prior written permission.
|
20
|
+
|
21
|
+
THIS SOFTWARE IS PROVIDED BY MEDIARIUM LTD. "AS IS" AND ANY EXPRESS OR IMPLIED
|
22
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
23
|
+
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MEDIARIUM LTD.
|
24
|
+
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
26
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
27
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
29
|
+
IF ADVISEDOF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
data/lib/ruby/commons.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'file/extname'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Hash
|
2
|
+
##
|
3
|
+
# Returns a hash with non +nil+ values.
|
4
|
+
#
|
5
|
+
# hash = { a: true, b: false, c: nil}
|
6
|
+
# hash.compact # => { a: true, b: false}
|
7
|
+
# hash # => { a: true, b: false, c: nil}
|
8
|
+
# { c: nil }.compact # => {}
|
9
|
+
def mp_compact
|
10
|
+
self.select { |_, value| !value.nil? }
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Replaces current hash with non +nil+ values.
|
15
|
+
#
|
16
|
+
# hash = { a: true, b: false, c: nil}
|
17
|
+
# hash.compact! # => { a: true, b: false}
|
18
|
+
# hash # => { a: true, b: false}
|
19
|
+
def mp_compact!
|
20
|
+
self.reject! { |_, value| value.nil? }
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# TODO
|
25
|
+
def mp_deep_compact!
|
26
|
+
proc_val = Proc.new {}
|
27
|
+
proc_hsh = Proc.new {}
|
28
|
+
|
29
|
+
proc_val = Proc.new do |v|
|
30
|
+
result = false
|
31
|
+
case v
|
32
|
+
when Hash
|
33
|
+
v.reject!(&proc_hsh)
|
34
|
+
when Array
|
35
|
+
v.each(&proc_val)
|
36
|
+
else
|
37
|
+
result = v.nil?
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
proc_hsh = Proc.new do |k, v|
|
43
|
+
proc_val.call(v)
|
44
|
+
end
|
45
|
+
|
46
|
+
delete_if(&proc_hsh)
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a hash that includes everything but the given keys.
|
3
|
+
# hash = { a: true, b: false, c: nil}
|
4
|
+
# hash.mp_except(:c) # => { a: true, b: false}
|
5
|
+
# hash # => { a: true, b: false, c: nil}
|
6
|
+
#
|
7
|
+
# This is useful for limiting a set of parameters to everything but a few known toggles:
|
8
|
+
# @person.update(params[:person].mp_except(:admin))
|
9
|
+
def mp_except(*keys)
|
10
|
+
dup.mp_except!(*keys)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Replaces the hash without the given keys.
|
14
|
+
# hash = { a: true, b: false, c: nil}
|
15
|
+
# hash.mp_except!(:c) # => { a: true, b: false}
|
16
|
+
# hash # => { a: true, b: false }
|
17
|
+
def mp_except!(*keys)
|
18
|
+
keys.each { |key| delete(key) }
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
class Hash
|
2
|
+
##
|
3
|
+
# Returns a new hash with all keys converted using the block operation.
|
4
|
+
#
|
5
|
+
# hash = { name: 'Rob', age: '28' }
|
6
|
+
#
|
7
|
+
# hash.mp_transform_keys { |key| key.to_s.upcase }
|
8
|
+
# # => {"NAME"=>"Rob", "AGE"=>"28"}
|
9
|
+
def mp_transform_keys
|
10
|
+
return enum_for(:mp_transform_keys) unless block_given?
|
11
|
+
result = self.class.new
|
12
|
+
each_key do |key|
|
13
|
+
result[yield(key)] = self[key]
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Destructively converts all keys using the block operations.
|
20
|
+
# Same as +mp_transform_keys+ but modifies +self+.
|
21
|
+
def mp_transform_keys!
|
22
|
+
return enum_for(:mp_transform_keys!) unless block_given?
|
23
|
+
keys.each do |key|
|
24
|
+
self[yield(key)] = delete(key)
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a new hash with all keys converted to strings.
|
31
|
+
#
|
32
|
+
# hash = { name: 'Rob', age: '28' }
|
33
|
+
#
|
34
|
+
# hash.mp_stringify_keys
|
35
|
+
# # => {"name"=>"Rob", "age"=>"28"}
|
36
|
+
def mp_stringify_keys
|
37
|
+
mp_transform_keys(&:to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Destructively converts all keys to strings. Same as
|
42
|
+
# +mp_stringify_keys+, but modifies +self+.
|
43
|
+
def mp_stringify_keys!
|
44
|
+
mp_transform_keys!(&:to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
49
|
+
# they respond to +to_sym+.
|
50
|
+
#
|
51
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
52
|
+
#
|
53
|
+
# hash.mp_symbolize_keys
|
54
|
+
# # => {:name=>"Rob", :age=>"28"}
|
55
|
+
def mp_symbolize_keys
|
56
|
+
mp_transform_keys { |key| key.to_sym rescue key }
|
57
|
+
end
|
58
|
+
alias_method :mp_to_options, :mp_symbolize_keys
|
59
|
+
|
60
|
+
##
|
61
|
+
# Destructively converts all keys to symbols, as long as they respond
|
62
|
+
# to +to_sym+. Same as +mp_symbolize_keys+, but modifies +self+.
|
63
|
+
def mp_symbolize_keys!
|
64
|
+
mp_transform_keys! { |key| key.to_sym rescue key }
|
65
|
+
end
|
66
|
+
alias_method :mp_to_options!, :mp_symbolize_keys!
|
67
|
+
|
68
|
+
##
|
69
|
+
# Returns a new hash with all keys converted by the block operation.
|
70
|
+
# This includes the keys from the root hash and from all
|
71
|
+
# nested hashes and arrays.
|
72
|
+
#
|
73
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
74
|
+
#
|
75
|
+
# hash.mp_deep_transform_keys { |key| key.to_s.upcase }
|
76
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
77
|
+
def mp_deep_transform_keys(&block)
|
78
|
+
__mp_deep_transform_keys_in_object(self, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Destructively converts all keys by using the block operation.
|
83
|
+
# This includes the keys from the root hash and from all
|
84
|
+
# nested hashes and arrays.
|
85
|
+
def mp_deep_transform_keys!(&block)
|
86
|
+
__mp_deep_transform_keys_in_object!(self, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Returns a new hash with all keys converted to strings.
|
91
|
+
# This includes the keys from the root hash and from all
|
92
|
+
# nested hashes and arrays.
|
93
|
+
#
|
94
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
95
|
+
#
|
96
|
+
# hash.mp_deep_stringify_keys
|
97
|
+
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
|
98
|
+
def mp_deep_stringify_keys
|
99
|
+
mp_deep_transform_keys(&:to_s)
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Destructively converts all keys to strings.
|
104
|
+
# This includes the keys from the root hash and from all
|
105
|
+
# nested hashes and arrays.
|
106
|
+
def mp_deep_stringify_keys!
|
107
|
+
mp_deep_transform_keys!(&:to_s)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
112
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
113
|
+
# and from all nested hashes and arrays.
|
114
|
+
#
|
115
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
116
|
+
#
|
117
|
+
# hash.mp_deep_symbolize_keys
|
118
|
+
# # => {:person=>{:name=>"Rob", :age=>"28"}}
|
119
|
+
def mp_deep_symbolize_keys
|
120
|
+
mp_deep_transform_keys { |key| key.to_sym rescue key }
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Destructively converts all keys to symbols, as long as they respond
|
125
|
+
# to +to_sym+. This includes the keys from the root hash and from all
|
126
|
+
# nested hashes and arrays.
|
127
|
+
def mp_deep_symbolize_keys!
|
128
|
+
mp_deep_transform_keys! { |key| key.to_sym rescue key }
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Support methods for deep transforming nested hashes and arrays
|
133
|
+
private
|
134
|
+
def __mp_deep_transform_keys_in_object(object, &block)
|
135
|
+
case object
|
136
|
+
when Hash
|
137
|
+
object.each_with_object({}) do |(key, value), result|
|
138
|
+
result[yield(key)] = __mp_deep_transform_keys_in_object(value, &block)
|
139
|
+
end
|
140
|
+
when Array
|
141
|
+
object.map { |e| __mp_deep_transform_keys_in_object(e, &block) }
|
142
|
+
else
|
143
|
+
object
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Support methods for deep transforming nested hashes and arrays
|
149
|
+
private
|
150
|
+
def __mp_deep_transform_keys_in_object!(object, &block)
|
151
|
+
case object
|
152
|
+
when Hash
|
153
|
+
object.keys.each do |key|
|
154
|
+
value = object.delete(key)
|
155
|
+
object[yield(key)] = __mp_deep_transform_keys_in_object!(value, &block)
|
156
|
+
end
|
157
|
+
object
|
158
|
+
when Array
|
159
|
+
object.map! { |e| __mp_deep_transform_keys_in_object!(e, &block) }
|
160
|
+
else
|
161
|
+
object
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Hash
|
2
|
+
##
|
3
|
+
# Slice a hash to include only the given keys. Returns a hash containing
|
4
|
+
# the given keys.
|
5
|
+
#
|
6
|
+
# { a: 1, b: 2, c: 3, d: 4 }.mp_slice(:a, :b)
|
7
|
+
# # => {:a=>1, :b=>2}
|
8
|
+
#
|
9
|
+
# This is useful for limiting an options hash to valid keys before
|
10
|
+
# passing to a method:
|
11
|
+
#
|
12
|
+
# def search(criteria = {})
|
13
|
+
# criteria.assert_valid_keys(:mass, :velocity, :time)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# search(options.mp_slice(:mass, :velocity, :time))
|
17
|
+
#
|
18
|
+
# If you have an array of keys you want to limit to, you should splat them:
|
19
|
+
#
|
20
|
+
# valid_keys = [:mass, :velocity, :time]
|
21
|
+
# search(options.mp_slice(*valid_keys))
|
22
|
+
def mp_slice(*keys)
|
23
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
24
|
+
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Replaces the hash with only the given keys.
|
29
|
+
# Returns a hash containing the removed key/value pairs.
|
30
|
+
#
|
31
|
+
# { a: 1, b: 2, c: 3, d: 4 }.mp_slice!(:a, :b)
|
32
|
+
# # => {:c=>3, :d=>4}
|
33
|
+
def mp_slice!(*keys)
|
34
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
35
|
+
omit = mp_slice(*self.keys - keys)
|
36
|
+
hash = mp_slice(*keys)
|
37
|
+
hash.default = default
|
38
|
+
hash.default_proc = default_proc if default_proc
|
39
|
+
replace(hash)
|
40
|
+
omit
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Removes and returns the key/value pairs matching the given keys.
|
45
|
+
#
|
46
|
+
# { a: 1, b: 2, c: 3, d: 4 }.mp_extract!(:a, :b) # => {:a=>1, :b=>2}
|
47
|
+
# { a: 1, b: 2 }.mp_extract!(:a, :x) # => {:a=>1}
|
48
|
+
def mp_extract!(*keys)
|
49
|
+
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class Hash
|
2
|
+
##
|
3
|
+
# Returns a new hash with the results of running +block+ once for every value.
|
4
|
+
# The keys are unchanged.
|
5
|
+
#
|
6
|
+
# { a: 1, b: 2, c: 3 }.mp_transform_values { |k,v| v * 2 }
|
7
|
+
# # => { a: 2, b: 4, c: 6 }
|
8
|
+
def mp_transform_values
|
9
|
+
return enum_for(:mp_transform_values) unless block_given?
|
10
|
+
result = self.class.new
|
11
|
+
each do |key, value|
|
12
|
+
result[key] = yield(key, value)
|
13
|
+
end
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Destructive +mp_transform_values+
|
19
|
+
def mp_transform_values!
|
20
|
+
return enum_for(:mp_transform_values!) unless block_given?
|
21
|
+
each do |key, value|
|
22
|
+
self[key] = yield(key, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Returns a new hash with all values converted by the block operation.
|
28
|
+
# This includes the values from the root hash and from all
|
29
|
+
# nested hashes and arrays.
|
30
|
+
#
|
31
|
+
# hash = { person: { name: 'Rob', gender: 'Male' } }
|
32
|
+
#
|
33
|
+
# hash.mp_deep_transform_values { |value| value.to_s.upcase }
|
34
|
+
# # => {:person=>{:name=>"ROB", :gender=>"MALE"}}
|
35
|
+
def mp_deep_transform_values(&block)
|
36
|
+
__mp_deep_transform_values_in_object(self, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Destructively converts all values by using the block operation.
|
41
|
+
# This includes the values from the root hash and from all
|
42
|
+
# nested hashes and arrays.
|
43
|
+
def mp_deep_transform_values!(&block)
|
44
|
+
__mp_deep_transform_values_in_object!(self, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# MARK: - Private Methods
|
48
|
+
|
49
|
+
##
|
50
|
+
# Support methods for deep transforming nested hashes and arrays
|
51
|
+
private
|
52
|
+
def __mp_deep_transform_values_in_object(object, &block)
|
53
|
+
case object
|
54
|
+
when Hash
|
55
|
+
object.each_with_object({}) do |(key, value), result|
|
56
|
+
result[key] = __mp_deep_transform_values_in_object(yield(key, value), &block)
|
57
|
+
end
|
58
|
+
when Array
|
59
|
+
object.map { |e| __mp_deep_transform_values_in_object(e, &block) }
|
60
|
+
else
|
61
|
+
object
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Support methods for deep transforming nested hashes and arrays
|
67
|
+
private
|
68
|
+
def __mp_deep_transform_values_in_object!(object, &block)
|
69
|
+
case object
|
70
|
+
when Hash
|
71
|
+
object.keys.each do |key|
|
72
|
+
value = object.delete(key)
|
73
|
+
object[key] = __mp_deep_transform_values_in_object!(yield(key, value), &block)
|
74
|
+
end
|
75
|
+
object
|
76
|
+
when Array
|
77
|
+
object.map! { |e| __mp_deep_transform_values_in_object!(e, &block) }
|
78
|
+
else
|
79
|
+
object
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# How to include a YAML file inside a YAML file?
|
2
|
+
# @link http://stackoverflow.com/a/2286760
|
3
|
+
|
4
|
+
module JSON
|
5
|
+
##
|
6
|
+
# TODO
|
7
|
+
def self.mp_include(file_name)
|
8
|
+
require 'erb'
|
9
|
+
ERB.new(IO.read(file_name)).result
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# TODO
|
14
|
+
def self.mp_load_erb(file_name)
|
15
|
+
JSON::load(JSON::mp_include(file_name))
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'numeric/integer'
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class Object
|
2
|
+
##
|
3
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
4
|
+
# For example, '', ' ', +nil+, [], and {} are all blank.
|
5
|
+
#
|
6
|
+
# This simplifies
|
7
|
+
#
|
8
|
+
# address.nil? || address.empty?
|
9
|
+
#
|
10
|
+
# to
|
11
|
+
#
|
12
|
+
# address.mp_blank?
|
13
|
+
#
|
14
|
+
# @return [true, false]
|
15
|
+
def mp_blank?
|
16
|
+
respond_to?(:empty?) ? !!empty? : !self
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# An object is present if it's not blank.
|
21
|
+
#
|
22
|
+
# @return [true, false]
|
23
|
+
def mp_present?
|
24
|
+
!mp_blank?
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
29
|
+
# <tt>object.mp_presence</tt> is equivalent to
|
30
|
+
#
|
31
|
+
# object.mp_present? ? object : nil
|
32
|
+
#
|
33
|
+
# For example, something like
|
34
|
+
#
|
35
|
+
# state = params[:state] if params[:state].mp_present?
|
36
|
+
# country = params[:country] if params[:country].mp_present?
|
37
|
+
# region = state || country || 'US'
|
38
|
+
#
|
39
|
+
# becomes
|
40
|
+
#
|
41
|
+
# region = params[:state].mp_presence || params[:country].mp_presence || 'US'
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
def mp_presence
|
45
|
+
self if mp_present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NilClass
|
50
|
+
##
|
51
|
+
# +nil+ is blank:
|
52
|
+
#
|
53
|
+
# nil.mp_blank? # => true
|
54
|
+
#
|
55
|
+
# @return [true]
|
56
|
+
def mp_blank?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class FalseClass
|
62
|
+
##
|
63
|
+
# +false+ is blank:
|
64
|
+
#
|
65
|
+
# false.mp_blank? # => true
|
66
|
+
#
|
67
|
+
# @return [true]
|
68
|
+
def mp_blank?
|
69
|
+
true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class TrueClass
|
74
|
+
##
|
75
|
+
# +true+ is not blank:
|
76
|
+
#
|
77
|
+
# true.mp_blank? # => false
|
78
|
+
#
|
79
|
+
# @return [false]
|
80
|
+
def mp_blank?
|
81
|
+
false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Array
|
86
|
+
##
|
87
|
+
# An array is blank if it's empty:
|
88
|
+
#
|
89
|
+
# [].mp_blank? # => true
|
90
|
+
# [1,2,3].mp_blank? # => false
|
91
|
+
#
|
92
|
+
# @return [true, false]
|
93
|
+
alias_method :mp_blank?, :empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
class Hash
|
97
|
+
##
|
98
|
+
# A hash is blank if it's empty:
|
99
|
+
#
|
100
|
+
# {}.mp_blank? # => true
|
101
|
+
# { key: 'value' }.mp_blank? # => false
|
102
|
+
#
|
103
|
+
# @return [true, false]
|
104
|
+
alias_method :mp_blank?, :empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
class String
|
108
|
+
MP_BLANK_RE = /\A[[:space:]]*\z/
|
109
|
+
|
110
|
+
##
|
111
|
+
# A string is blank if it's empty or contains whitespaces only:
|
112
|
+
#
|
113
|
+
# ''.mp_blank? # => true
|
114
|
+
# ' '.mp_blank? # => true
|
115
|
+
# "\t\n\r".mp_blank? # => true
|
116
|
+
# ' blah '.mp_blank? # => false
|
117
|
+
#
|
118
|
+
# Unicode whitespace is supported:
|
119
|
+
#
|
120
|
+
# "\u00a0".mp_blank? # => true
|
121
|
+
#
|
122
|
+
# @return [true, false]
|
123
|
+
def mp_blank?
|
124
|
+
MP_BLANK_RE === self
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Numeric #:nodoc:
|
129
|
+
##
|
130
|
+
# No number is blank:
|
131
|
+
#
|
132
|
+
# 1.mp_blank? # => false
|
133
|
+
# 0.mp_blank? # => false
|
134
|
+
#
|
135
|
+
# @return [false]
|
136
|
+
def mp_blank?
|
137
|
+
false
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'duplicable'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
##
|
5
|
+
# Returns a deep copy of object if it's duplicable. If it's
|
6
|
+
# not duplicable, returns +self+.
|
7
|
+
#
|
8
|
+
# object = Object.new
|
9
|
+
# dup = object.mp_deep_dup
|
10
|
+
# dup.instance_variable_set(:@a, 1)
|
11
|
+
#
|
12
|
+
# object.instance_variable_defined?(:@a) # => false
|
13
|
+
# dup.instance_variable_defined?(:@a) # => true
|
14
|
+
def mp_deep_dup
|
15
|
+
mp_duplicable? ? dup : self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Array
|
20
|
+
##
|
21
|
+
# Returns a deep copy of array.
|
22
|
+
#
|
23
|
+
# array = [1, [2, 3]]
|
24
|
+
# dup = array.mp_deep_dup
|
25
|
+
# dup[1][2] = 4
|
26
|
+
#
|
27
|
+
# array[1][2] # => nil
|
28
|
+
# dup[1][2] # => 4
|
29
|
+
def mp_deep_dup
|
30
|
+
map(&:mp_deep_dup)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Hash
|
35
|
+
##
|
36
|
+
# Returns a deep copy of hash.
|
37
|
+
#
|
38
|
+
# hash = { a: { b: 'b' } }
|
39
|
+
# dup = hash.mp_deep_dup
|
40
|
+
# dup[:a][:c] = 'c'
|
41
|
+
#
|
42
|
+
# hash[:a][:c] # => nil
|
43
|
+
# dup[:a][:c] # => "c"
|
44
|
+
def mp_deep_dup
|
45
|
+
each_with_object(dup) do |(key, value), hash|
|
46
|
+
hash.delete(key)
|
47
|
+
hash[key.mp_deep_dup] = value.mp_deep_dup
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
##
|
2
|
+
# Most objects are cloneable, but not all. For example you can't dup +nil+:
|
3
|
+
#
|
4
|
+
# nil.dup # => TypeError: can't dup NilClass
|
5
|
+
#
|
6
|
+
# Classes may signal their instances are not duplicable removing +dup+/+clone+
|
7
|
+
# or raising exceptions from them. So, to dup an arbitrary object you normally
|
8
|
+
# use an optimistic approach and are ready to catch an exception, say:
|
9
|
+
#
|
10
|
+
# arbitrary_object.dup rescue object
|
11
|
+
#
|
12
|
+
# Rails dups objects in a few critical spots where they are not that arbitrary.
|
13
|
+
# That rescue is very expensive (like 40 times slower than a predicate), and it
|
14
|
+
# is often triggered.
|
15
|
+
#
|
16
|
+
# That's why we hardcode the following cases and check +mp_duplicable?+ instead
|
17
|
+
# of using that rescue idiom.
|
18
|
+
class Object
|
19
|
+
##
|
20
|
+
# Can you safely dup this object?
|
21
|
+
#
|
22
|
+
# False for +nil+, +false+, +true+, symbol, number objects;
|
23
|
+
# true otherwise.
|
24
|
+
def mp_duplicable?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class NilClass
|
30
|
+
##
|
31
|
+
# +nil+ is not duplicable:
|
32
|
+
#
|
33
|
+
# nil.mp_duplicable? # => false
|
34
|
+
# nil.dup # => TypeError: can't dup NilClass
|
35
|
+
def mp_duplicable?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class FalseClass
|
41
|
+
##
|
42
|
+
# +false+ is not duplicable:
|
43
|
+
#
|
44
|
+
# false.mp_duplicable? # => false
|
45
|
+
# false.dup # => TypeError: can't dup FalseClass
|
46
|
+
def mp_duplicable?
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class TrueClass
|
52
|
+
##
|
53
|
+
# +true+ is not duplicable:
|
54
|
+
#
|
55
|
+
# true.mp_duplicable? # => false
|
56
|
+
# true.dup # => TypeError: can't dup TrueClass
|
57
|
+
def mp_duplicable?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Symbol
|
63
|
+
##
|
64
|
+
# Symbols are not duplicable:
|
65
|
+
#
|
66
|
+
# :my_symbol.mp_duplicable? # => false
|
67
|
+
# :my_symbol.dup # => TypeError: can't dup Symbol
|
68
|
+
def mp_duplicable?
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Numeric
|
74
|
+
##
|
75
|
+
# Numbers are not duplicable:
|
76
|
+
#
|
77
|
+
# 3.mp_duplicable? # => false
|
78
|
+
# 3.dup # => TypeError: can't dup Fixnum
|
79
|
+
def mp_duplicable?
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
require 'bigdecimal'
|
85
|
+
class BigDecimal
|
86
|
+
def mp_duplicable?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Method
|
92
|
+
##
|
93
|
+
# Methods are not duplicable:
|
94
|
+
#
|
95
|
+
# method(:puts).mp_duplicable? # => false
|
96
|
+
# method(:puts).dup # => TypeError: allocator undefined for Method
|
97
|
+
def mp_duplicable?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Object
|
2
|
+
##
|
3
|
+
# TODO
|
4
|
+
def mp_deep_freeze
|
5
|
+
proc_val = Proc.new {}
|
6
|
+
|
7
|
+
proc_hsh = Proc.new do |k, v|
|
8
|
+
proc_val.call(v)
|
9
|
+
end
|
10
|
+
|
11
|
+
proc_val = Proc.new do |v|
|
12
|
+
case v
|
13
|
+
when Hash
|
14
|
+
v.each_value(&proc_hsh)
|
15
|
+
when Array
|
16
|
+
v.each(&proc_val)
|
17
|
+
else
|
18
|
+
# Do nothing ..
|
19
|
+
end
|
20
|
+
|
21
|
+
v.freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
proc_val.call(self)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Object
|
2
|
+
##
|
3
|
+
# Gets a value from an Object using a dot separated path.
|
4
|
+
#
|
5
|
+
# { a: 1, b: { c: 3, d: [1, 2, 3] } }.mp_path('b.d') # => [1, 2, 3]
|
6
|
+
# { a: 1, b: { c: 3, d: [1, 2, 3] } }.mp_path('b.d.1') # => 2
|
7
|
+
def mp_path(path = '', default = nil, delimiter = '.')
|
8
|
+
value = self
|
9
|
+
|
10
|
+
path.to_s.split(delimiter).each do |key|
|
11
|
+
index = key.to_i
|
12
|
+
|
13
|
+
unless value.respond_to?(:[])
|
14
|
+
value = nil
|
15
|
+
break
|
16
|
+
end
|
17
|
+
|
18
|
+
if key == index.to_s
|
19
|
+
value = value[index]
|
20
|
+
elsif value.respond_to?(:key?)
|
21
|
+
value = value.key?(key) ? value[key] : value[key.to_sym]
|
22
|
+
else
|
23
|
+
value = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
break if value.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
value || default
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class Object
|
2
|
+
##
|
3
|
+
# Invokes the public method whose name goes as first argument just like
|
4
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
5
|
+
# call returns +nil+ rather than raising an exception.
|
6
|
+
#
|
7
|
+
# This method is defined to be able to write
|
8
|
+
#
|
9
|
+
# @person.mp_try(:name)
|
10
|
+
#
|
11
|
+
# instead of
|
12
|
+
#
|
13
|
+
# @person.name if @person
|
14
|
+
#
|
15
|
+
# +mp_try+ calls can be chained:
|
16
|
+
#
|
17
|
+
# @person.mp_try(:spouse).mp_try(:name)
|
18
|
+
#
|
19
|
+
# instead of
|
20
|
+
#
|
21
|
+
# @person.spouse.name if @person && @person.spouse
|
22
|
+
#
|
23
|
+
# +mp_try+ will also return +nil+ if the receiver does not respond to the method:
|
24
|
+
#
|
25
|
+
# @person.mp_try(:non_existing_method) #=> nil
|
26
|
+
#
|
27
|
+
# instead of
|
28
|
+
#
|
29
|
+
# @person.non_existing_method if @person.respond_to?(:non_existing_method) #=> nil
|
30
|
+
#
|
31
|
+
# +mp_try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
32
|
+
# to the method:
|
33
|
+
#
|
34
|
+
# nil.mp_try(:to_i) # => nil, rather than 0
|
35
|
+
#
|
36
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
37
|
+
#
|
38
|
+
# @posts.mp_try(:each_slice, 2) do |a, b|
|
39
|
+
# ...
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The number of arguments in the signature must match. If the object responds
|
43
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
44
|
+
# in case of argument mismatch.
|
45
|
+
#
|
46
|
+
# If +mp_try+ is called without arguments it yields the receiver to a given
|
47
|
+
# block unless it is +nil+:
|
48
|
+
#
|
49
|
+
# @person.mp_try do |p|
|
50
|
+
# ...
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# You can also call mp_try with a block without accepting an argument, and the block
|
54
|
+
# will be instance_eval'ed instead:
|
55
|
+
#
|
56
|
+
# @person.mp_try { upcase.truncate(50) }
|
57
|
+
#
|
58
|
+
# Please also note that +mp_try+ is defined on +Object+. Therefore, it won't work
|
59
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
60
|
+
# like direct subclasses of +BasicObject+. For example, using +mp_try+ with
|
61
|
+
# +SimpleDelegator+ will delegate +mp_try+ to the target instead of calling it on
|
62
|
+
# the delegator itself.
|
63
|
+
def mp_try(*a, &b)
|
64
|
+
mp_try!(*a, &b) if a.empty? || respond_to?(a.first)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Same as #mp_try, but will raise a NoMethodError exception if the receiver is not +nil+ and
|
69
|
+
# does not implement the tried method.
|
70
|
+
def mp_try!(*a, &b)
|
71
|
+
if a.empty? && block_given?
|
72
|
+
if b.arity.zero?
|
73
|
+
instance_eval(&b)
|
74
|
+
else
|
75
|
+
yield self
|
76
|
+
end
|
77
|
+
else
|
78
|
+
public_send(*a, &b)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class NilClass
|
84
|
+
##
|
85
|
+
# Calling +mp_try+ on +nil+ always returns +nil+.
|
86
|
+
# It becomes especially helpful when navigating through associations that may return +nil+.
|
87
|
+
#
|
88
|
+
# nil.mp_try(:name) # => nil
|
89
|
+
#
|
90
|
+
# Without +mp_try+
|
91
|
+
# @person && @person.children.any? && @person.children.first.name
|
92
|
+
#
|
93
|
+
# With +mp_try+
|
94
|
+
# @person.mp_try(:children).mp_try(:first).mp_try(:name)
|
95
|
+
def mp_try(*args)
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def mp_try!(*args)
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'regexp/match'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class String
|
2
|
+
##
|
3
|
+
# TODO
|
4
|
+
def mp_trim(character_set)
|
5
|
+
mp_ltrim(character_set).mp_rtrim(character_set)
|
6
|
+
end
|
7
|
+
|
8
|
+
##
|
9
|
+
# TODO
|
10
|
+
def mp_ltrim(character_set)
|
11
|
+
sub(/^[#{character_set}]+/, '')
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# TODO
|
16
|
+
def mp_rtrim(character_set)
|
17
|
+
sub(/[#{character_set}]+$/, '')
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ruby::Commons
|
2
|
+
|
3
|
+
# Returns the version of the currently loaded Ruby::Commons as a <tt>Gem::Version</tt>
|
4
|
+
def self.gem_version
|
5
|
+
Gem::Version.new VERSION::STRING
|
6
|
+
end
|
7
|
+
|
8
|
+
module VERSION
|
9
|
+
MAJOR = 0
|
10
|
+
MINOR = 1
|
11
|
+
TINY = 0
|
12
|
+
PRE = nil
|
13
|
+
|
14
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/ruby_commons.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ruby/commons'
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-commons
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Bragin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A toolkit of support libraries and Ruby core extensions.
|
14
|
+
email: alexander.bragin@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- LICENSE.txt
|
20
|
+
- README.rdoc
|
21
|
+
- lib/ruby/commons.rb
|
22
|
+
- lib/ruby/commons/core_ext.rb
|
23
|
+
- lib/ruby/commons/core_ext/file.rb
|
24
|
+
- lib/ruby/commons/core_ext/file/extname.rb
|
25
|
+
- lib/ruby/commons/core_ext/hash.rb
|
26
|
+
- lib/ruby/commons/core_ext/hash/compact.rb
|
27
|
+
- lib/ruby/commons/core_ext/hash/except.rb
|
28
|
+
- lib/ruby/commons/core_ext/hash/keys.rb
|
29
|
+
- lib/ruby/commons/core_ext/hash/slice.rb
|
30
|
+
- lib/ruby/commons/core_ext/hash/values.rb
|
31
|
+
- lib/ruby/commons/core_ext/json.rb
|
32
|
+
- lib/ruby/commons/core_ext/json/include.rb
|
33
|
+
- lib/ruby/commons/core_ext/numeric.rb
|
34
|
+
- lib/ruby/commons/core_ext/numeric/integer.rb
|
35
|
+
- lib/ruby/commons/core_ext/object.rb
|
36
|
+
- lib/ruby/commons/core_ext/object/blank.rb
|
37
|
+
- lib/ruby/commons/core_ext/object/deep_dup.rb
|
38
|
+
- lib/ruby/commons/core_ext/object/duplicable.rb
|
39
|
+
- lib/ruby/commons/core_ext/object/freeze.rb
|
40
|
+
- lib/ruby/commons/core_ext/object/path.rb
|
41
|
+
- lib/ruby/commons/core_ext/object/try.rb
|
42
|
+
- lib/ruby/commons/core_ext/regexp.rb
|
43
|
+
- lib/ruby/commons/core_ext/regexp/match.rb
|
44
|
+
- lib/ruby/commons/core_ext/string.rb
|
45
|
+
- lib/ruby/commons/core_ext/string/match.rb
|
46
|
+
- lib/ruby/commons/core_ext/string/trim.rb
|
47
|
+
- lib/ruby/commons/core_ext/uri.rb
|
48
|
+
- lib/ruby/commons/core_ext/uri/parse.rb
|
49
|
+
- lib/ruby/commons/error.rb
|
50
|
+
- lib/ruby/commons/error/abstract_class_error.rb
|
51
|
+
- lib/ruby/commons/gem_version.rb
|
52
|
+
- lib/ruby/commons/version.rb
|
53
|
+
- lib/ruby_commons.rb
|
54
|
+
homepage: http://www.mediarium.com
|
55
|
+
licenses:
|
56
|
+
- BSD-4-Clause
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options:
|
60
|
+
- "--exclude"
|
61
|
+
- "."
|
62
|
+
require_paths:
|
63
|
+
- config
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.3.0
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 2.4.0
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.5.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: A toolkit of support libraries and Ruby core extensions.
|
81
|
+
test_files: []
|