ruby-commons 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|