ruby-commons 0.1.0

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