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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +29 -0
  3. data/README.rdoc +3 -0
  4. data/lib/ruby/commons.rb +9 -0
  5. data/lib/ruby/commons/core_ext.rb +2 -0
  6. data/lib/ruby/commons/core_ext/file.rb +1 -0
  7. data/lib/ruby/commons/core_ext/file/extname.rb +8 -0
  8. data/lib/ruby/commons/core_ext/hash.rb +5 -0
  9. data/lib/ruby/commons/core_ext/hash/compact.rb +48 -0
  10. data/lib/ruby/commons/core_ext/hash/except.rb +21 -0
  11. data/lib/ruby/commons/core_ext/hash/keys.rb +164 -0
  12. data/lib/ruby/commons/core_ext/hash/slice.rb +51 -0
  13. data/lib/ruby/commons/core_ext/hash/values.rb +82 -0
  14. data/lib/ruby/commons/core_ext/json.rb +3 -0
  15. data/lib/ruby/commons/core_ext/json/include.rb +17 -0
  16. data/lib/ruby/commons/core_ext/numeric.rb +1 -0
  17. data/lib/ruby/commons/core_ext/numeric/integer.rb +14 -0
  18. data/lib/ruby/commons/core_ext/object.rb +6 -0
  19. data/lib/ruby/commons/core_ext/object/blank.rb +139 -0
  20. data/lib/ruby/commons/core_ext/object/deep_dup.rb +50 -0
  21. data/lib/ruby/commons/core_ext/object/duplicable.rb +100 -0
  22. data/lib/ruby/commons/core_ext/object/freeze.rb +26 -0
  23. data/lib/ruby/commons/core_ext/object/path.rb +31 -0
  24. data/lib/ruby/commons/core_ext/object/try.rb +102 -0
  25. data/lib/ruby/commons/core_ext/regexp.rb +1 -0
  26. data/lib/ruby/commons/core_ext/regexp/match.rb +7 -0
  27. data/lib/ruby/commons/core_ext/string.rb +2 -0
  28. data/lib/ruby/commons/core_ext/string/match.rb +7 -0
  29. data/lib/ruby/commons/core_ext/string/trim.rb +19 -0
  30. data/lib/ruby/commons/core_ext/uri.rb +3 -0
  31. data/lib/ruby/commons/core_ext/uri/parse.rb +11 -0
  32. data/lib/ruby/commons/error.rb +2 -0
  33. data/lib/ruby/commons/error/abstract_class_error.rb +3 -0
  34. data/lib/ruby/commons/gem_version.rb +17 -0
  35. data/lib/ruby/commons/version.rb +10 -0
  36. data/lib/ruby_commons.rb +1 -0
  37. 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
@@ -0,0 +1,3 @@
1
+ = Ruby::Commons
2
+
3
+ A toolkit of support libraries and Ruby core extensions.
@@ -0,0 +1,9 @@
1
+ module Ruby
2
+ module Commons
3
+ # Do nothing
4
+ end
5
+ end
6
+
7
+ require 'ruby/commons/core_ext'
8
+ require 'ruby/commons/error'
9
+ require 'ruby/commons/version'
@@ -0,0 +1,2 @@
1
+ # Require all Core Extensions (monkey patches) at once
2
+ Dir[File.join(File.dirname(__FILE__), 'core_ext', '*.rb')].each { |path| require path }
@@ -0,0 +1 @@
1
+ require_relative 'file/extname'
@@ -0,0 +1,8 @@
1
+ class File
2
+ ##
3
+ # TODO
4
+ def self.mp_extension(path)
5
+ extension = File.extname(path || '')
6
+ extension[1..-1] if extension.mp_present?
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'hash/compact'
2
+ require_relative 'hash/except'
3
+ require_relative 'hash/keys'
4
+ require_relative 'hash/slice'
5
+ require_relative 'hash/values'
@@ -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,3 @@
1
+ require 'json'
2
+
3
+ require_relative 'json/include'
@@ -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,14 @@
1
+ class Numeric
2
+ ##
3
+ # TODO
4
+ def self.mp_is_int?(obj)
5
+ value = obj.to_s
6
+ value.to_i.to_s == value
7
+ end
8
+
9
+ ##
10
+ # TODO
11
+ def self.mp_to_i(value, default = nil)
12
+ mp_is_int?(value) ? value.to_i : default
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'object/blank'
2
+ require_relative 'object/duplicable'
3
+ require_relative 'object/deep_dup'
4
+ require_relative 'object/freeze'
5
+ require_relative 'object/path'
6
+ require_relative 'object/try'
@@ -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,7 @@
1
+ class Regexp
2
+ ##
3
+ # TODO
4
+ def mp_match?(*several_variants)
5
+ !match(*several_variants).nil?
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'string/match'
2
+ require_relative 'string/trim'
@@ -0,0 +1,7 @@
1
+ class String
2
+ ##
3
+ # TODO
4
+ def mp_match?(*several_variants)
5
+ !match(*several_variants).nil?
6
+ end
7
+ end
@@ -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,3 @@
1
+ require 'uri'
2
+
3
+ require_relative 'uri/parse'
@@ -0,0 +1,11 @@
1
+ module URI
2
+ ##
3
+ # TODO
4
+ def self.mp_try_parse(uri)
5
+ begin
6
+ return URI(uri)
7
+ rescue ArgumentError, InvalidURIError
8
+ return nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ # Require all Errors at once
2
+ Dir[File.join(File.dirname(__FILE__), 'error', '*.rb')].each { |path| require path }
@@ -0,0 +1,3 @@
1
+ class AbstractClassError < StandardError
2
+ # Do nothing
3
+ 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
@@ -0,0 +1,10 @@
1
+ require_relative 'gem_version'
2
+
3
+ module Ruby::Commons
4
+
5
+ # Returns the version of the currently loaded Ruby::Commons as a string.
6
+ def self.version
7
+ VERSION::STRING
8
+ end
9
+
10
+ end
@@ -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: []