simple_ext 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/lib/simple_ext/array/access.rb +122 -0
- data/lib/simple_ext/array.rb +3 -0
- data/lib/simple_ext/digest/uuid.rb +53 -0
- data/lib/simple_ext/digest.rb +3 -0
- data/lib/simple_ext/file/atomic.rb +70 -0
- data/lib/simple_ext/file.rb +3 -0
- data/lib/simple_ext/hash/except.rb +22 -0
- data/lib/simple_ext/hash/keys.rb +143 -0
- data/lib/simple_ext/hash/merge.rb +56 -0
- data/lib/simple_ext/hash/slice.rb +26 -0
- data/lib/simple_ext/hash/values.rb +46 -0
- data/lib/simple_ext/hash.rb +7 -0
- data/lib/simple_ext/numeric/bytes.rb +66 -0
- data/lib/simple_ext/numeric.rb +3 -0
- data/lib/simple_ext/object/blank.rb +154 -0
- data/lib/simple_ext/object/inclusion.rb +29 -0
- data/lib/simple_ext/object/instance_variables.rb +30 -0
- data/lib/simple_ext/object/to_query.rb +89 -0
- data/lib/simple_ext/object/try.rb +127 -0
- data/lib/simple_ext/object.rb +7 -0
- data/lib/simple_ext.rb +5 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5f972070633ea700b9c69fc42ed84258ed68bbfe3fb95df8284c62dc3342db42
|
4
|
+
data.tar.gz: 7e19bc443f5f7ddc6658aaf20936ccfce95e108e4e6498e489d68ec1f7da9973
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8778c40b09e43907f4da9cbfb7473a2d6add060632025925a77553daf61eb58249fe3f4afea0238d59ca0a29596207284d2388682c7fc70a7092d108247cfaa4
|
7
|
+
data.tar.gz: 8723bbf6360f6637382acae47b95d82bb1b7f6dddfcf8e0605670c449960ac0c36edb60621ed8d480c3fdbc441106cecb77655126328d8b4e58e72d385ea9f65
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Array
|
4
|
+
# Removes and returns the elements for which the block returns a true value.
|
5
|
+
# If no block is given, an Enumerator is returned instead.
|
6
|
+
#
|
7
|
+
# numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
8
|
+
# odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
|
9
|
+
# numbers # => [0, 2, 4, 6, 8]
|
10
|
+
def extract!
|
11
|
+
return to_enum(:extract!) { size } unless block_given?
|
12
|
+
|
13
|
+
extracted_elements = []
|
14
|
+
|
15
|
+
reject! do |element|
|
16
|
+
extracted_elements << element if yield(element)
|
17
|
+
end
|
18
|
+
|
19
|
+
extracted_elements
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the tail of the array from +position+.
|
23
|
+
#
|
24
|
+
# %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
|
25
|
+
# %w( a b c d ).from(2) # => ["c", "d"]
|
26
|
+
# %w( a b c d ).from(10) # => []
|
27
|
+
# %w().from(0) # => []
|
28
|
+
# %w( a b c d ).from(-2) # => ["c", "d"]
|
29
|
+
# %w( a b c ).from(-10) # => []
|
30
|
+
def from(position)
|
31
|
+
self[position, length] || []
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the beginning of the array up to +position+.
|
35
|
+
#
|
36
|
+
# %w( a b c d ).to(0) # => ["a"]
|
37
|
+
# %w( a b c d ).to(2) # => ["a", "b", "c"]
|
38
|
+
# %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
|
39
|
+
# %w().to(0) # => []
|
40
|
+
# %w( a b c d ).to(-2) # => ["a", "b", "c"]
|
41
|
+
# %w( a b c ).to(-10) # => []
|
42
|
+
def to(position)
|
43
|
+
if position >= 0
|
44
|
+
take position + 1
|
45
|
+
else
|
46
|
+
self[0..position]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a new array that includes the passed elements.
|
51
|
+
#
|
52
|
+
# [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
|
53
|
+
# [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
|
54
|
+
def including(*elements)
|
55
|
+
self + elements.flatten(1)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a copy of the Array excluding the specified elements.
|
59
|
+
#
|
60
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
|
61
|
+
# [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
|
62
|
+
#
|
63
|
+
# Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
|
64
|
+
# instead of <tt>Array#reject</tt> for performance reasons.
|
65
|
+
def excluding(*elements)
|
66
|
+
self - elements.flatten(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Alias for #excluding.
|
70
|
+
def without(*elements)
|
71
|
+
excluding(*elements)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Equal to <tt>self[1]</tt>.
|
75
|
+
#
|
76
|
+
# %w( a b c d e ).second # => "b"
|
77
|
+
def second
|
78
|
+
self[1]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Equal to <tt>self[2]</tt>.
|
82
|
+
#
|
83
|
+
# %w( a b c d e ).third # => "c"
|
84
|
+
def third
|
85
|
+
self[2]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Equal to <tt>self[3]</tt>.
|
89
|
+
#
|
90
|
+
# %w( a b c d e ).fourth # => "d"
|
91
|
+
def fourth
|
92
|
+
self[3]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Equal to <tt>self[4]</tt>.
|
96
|
+
#
|
97
|
+
# %w( a b c d e ).fifth # => "e"
|
98
|
+
def fifth
|
99
|
+
self[4]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
|
103
|
+
#
|
104
|
+
# (1..42).to_a.forty_two # => 42
|
105
|
+
def forty_two
|
106
|
+
self[41]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Equal to <tt>self[-3]</tt>.
|
110
|
+
#
|
111
|
+
# %w( a b c d e ).third_to_last # => "c"
|
112
|
+
def third_to_last
|
113
|
+
self[-3]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Equal to <tt>self[-2]</tt>.
|
117
|
+
#
|
118
|
+
# %w( a b c d e ).second_to_last # => "d"
|
119
|
+
def second_to_last
|
120
|
+
self[-2]
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Digest
|
6
|
+
module UUID
|
7
|
+
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
|
8
|
+
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
|
9
|
+
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
|
10
|
+
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
|
11
|
+
|
12
|
+
# Generates a v5 non-random UUID (Universally Unique IDentifier).
|
13
|
+
#
|
14
|
+
# Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
|
15
|
+
# uuid_from_hash always generates the same UUID for a given name and namespace combination.
|
16
|
+
#
|
17
|
+
# See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
|
18
|
+
def self.uuid_from_hash(hash_class, uuid_namespace, name)
|
19
|
+
if hash_class == Digest::MD5
|
20
|
+
version = 3
|
21
|
+
elsif hash_class == Digest::SHA1
|
22
|
+
version = 5
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
|
25
|
+
end
|
26
|
+
|
27
|
+
hash = hash_class.new
|
28
|
+
hash.update(uuid_namespace)
|
29
|
+
hash.update(name)
|
30
|
+
|
31
|
+
ary = hash.digest.unpack("NnnnnN")
|
32
|
+
ary[2] = (ary[2] & 0x0FFF) | (version << 12)
|
33
|
+
ary[3] = (ary[3] & 0x3FFF) | 0x8000
|
34
|
+
|
35
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % ary
|
36
|
+
end
|
37
|
+
|
38
|
+
# Convenience method for uuid_from_hash using Digest::MD5.
|
39
|
+
def self.uuid_v3(uuid_namespace, name)
|
40
|
+
uuid_from_hash(Digest::MD5, uuid_namespace, name)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convenience method for uuid_from_hash using Digest::SHA1.
|
44
|
+
def self.uuid_v5(uuid_namespace, name)
|
45
|
+
uuid_from_hash(Digest::SHA1, uuid_namespace, name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Convenience method for SecureRandom.uuid.
|
49
|
+
def self.uuid_v4
|
50
|
+
SecureRandom.uuid
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
class File
|
6
|
+
# Write to a file atomically. Useful for situations where you don't
|
7
|
+
# want other processes or threads to see half-written files.
|
8
|
+
#
|
9
|
+
# File.atomic_write('important.file') do |file|
|
10
|
+
# file.write('hello')
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# This method needs to create a temporary file. By default it will create it
|
14
|
+
# in the same directory as the destination file. If you don't like this
|
15
|
+
# behavior you can provide a different directory but it must be on the
|
16
|
+
# same physical filesystem as the file you're trying to write.
|
17
|
+
#
|
18
|
+
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
19
|
+
# file.write('hello')
|
20
|
+
# end
|
21
|
+
def self.atomic_write(file_name, temp_dir = dirname(file_name))
|
22
|
+
require "tempfile" unless defined?(Tempfile)
|
23
|
+
|
24
|
+
Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
|
25
|
+
temp_file.binmode
|
26
|
+
return_val = yield temp_file
|
27
|
+
temp_file.close
|
28
|
+
|
29
|
+
old_stat = if exist?(file_name)
|
30
|
+
# Get original file permissions
|
31
|
+
stat(file_name)
|
32
|
+
else
|
33
|
+
# If not possible, probe which are the default permissions in the
|
34
|
+
# destination directory.
|
35
|
+
probe_stat_in(dirname(file_name))
|
36
|
+
end
|
37
|
+
|
38
|
+
if old_stat
|
39
|
+
# Set correct permissions on new file
|
40
|
+
begin
|
41
|
+
chown(old_stat.uid, old_stat.gid, temp_file.path)
|
42
|
+
# This operation will affect filesystem ACL's
|
43
|
+
chmod(old_stat.mode, temp_file.path)
|
44
|
+
rescue Errno::EPERM, Errno::EACCES
|
45
|
+
# Changing file ownership failed, moving on.
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Overwrite original file with temp file
|
50
|
+
rename(temp_file.path, file_name)
|
51
|
+
return_val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Private utility method.
|
56
|
+
def self.probe_stat_in(dir) #:nodoc:
|
57
|
+
basename = [
|
58
|
+
".permissions_check",
|
59
|
+
Thread.current.object_id,
|
60
|
+
Process.pid,
|
61
|
+
rand(1000000)
|
62
|
+
].join(".")
|
63
|
+
|
64
|
+
file_name = join(dir, basename)
|
65
|
+
FileUtils.touch(file_name)
|
66
|
+
stat(file_name)
|
67
|
+
ensure
|
68
|
+
FileUtils.rm_f(file_name) if file_name
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a hash that includes everything except given keys.
|
3
|
+
# hash = { a: true, b: false, c: nil }
|
4
|
+
# hash.except(:c) # => { a: true, b: false }
|
5
|
+
# hash.except(:a, :b) # => { c: nil }
|
6
|
+
# hash # => { a: true, b: false, c: nil }
|
7
|
+
#
|
8
|
+
# This is useful for limiting a set of parameters to everything but a few known toggles:
|
9
|
+
# @person.update(params[:person].except(:admin))
|
10
|
+
def except(*keys)
|
11
|
+
slice(*self.keys - keys)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Removes the given keys from hash and returns it.
|
15
|
+
# hash = { a: true, b: false, c: nil }
|
16
|
+
# hash.except!(:c) # => { a: true, b: false }
|
17
|
+
# hash # => { a: true, b: false }
|
18
|
+
def except!(*keys)
|
19
|
+
keys.each { |key| delete(key) }
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Returns a new hash with all keys converted to strings.
|
5
|
+
#
|
6
|
+
# hash = { name: 'Rob', age: '28' }
|
7
|
+
#
|
8
|
+
# hash.stringify_keys
|
9
|
+
# # => {"name"=>"Rob", "age"=>"28"}
|
10
|
+
def stringify_keys
|
11
|
+
transform_keys(&:to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Destructively converts all keys to strings. Same as
|
15
|
+
# +stringify_keys+, but modifies +self+.
|
16
|
+
def stringify_keys!
|
17
|
+
transform_keys!(&:to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
21
|
+
# they respond to +to_sym+.
|
22
|
+
#
|
23
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
24
|
+
#
|
25
|
+
# hash.symbolize_keys
|
26
|
+
# # => {:name=>"Rob", :age=>"28"}
|
27
|
+
def symbolize_keys
|
28
|
+
transform_keys { |key| key.to_sym rescue key }
|
29
|
+
end
|
30
|
+
alias_method :to_options, :symbolize_keys
|
31
|
+
|
32
|
+
# Destructively converts all keys to symbols, as long as they respond
|
33
|
+
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
|
34
|
+
def symbolize_keys!
|
35
|
+
transform_keys! { |key| key.to_sym rescue key }
|
36
|
+
end
|
37
|
+
alias_method :to_options!, :symbolize_keys!
|
38
|
+
|
39
|
+
# Validates all keys in a hash match <tt>*valid_keys</tt>, raising
|
40
|
+
# +ArgumentError+ on a mismatch.
|
41
|
+
#
|
42
|
+
# Note that keys are treated differently than HashWithIndifferentAccess,
|
43
|
+
# meaning that string and symbol keys will not match.
|
44
|
+
#
|
45
|
+
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
|
46
|
+
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
|
47
|
+
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
48
|
+
def assert_valid_keys(*valid_keys)
|
49
|
+
valid_keys.flatten!
|
50
|
+
each_key do |k|
|
51
|
+
unless valid_keys.include?(k)
|
52
|
+
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a new hash with all keys converted by the block operation.
|
58
|
+
# This includes the keys from the root hash and from all
|
59
|
+
# nested hashes and arrays.
|
60
|
+
#
|
61
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
62
|
+
#
|
63
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
64
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
65
|
+
def deep_transform_keys(&block)
|
66
|
+
_deep_transform_keys_in_object(self, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Destructively converts all keys by using the block operation.
|
70
|
+
# This includes the keys from the root hash and from all
|
71
|
+
# nested hashes and arrays.
|
72
|
+
def deep_transform_keys!(&block)
|
73
|
+
_deep_transform_keys_in_object!(self, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a new hash with all keys converted to strings.
|
77
|
+
# This includes the keys from the root hash and from all
|
78
|
+
# nested hashes and arrays.
|
79
|
+
#
|
80
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
81
|
+
#
|
82
|
+
# hash.deep_stringify_keys
|
83
|
+
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
|
84
|
+
def deep_stringify_keys
|
85
|
+
deep_transform_keys(&:to_s)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Destructively converts all keys to strings.
|
89
|
+
# This includes the keys from the root hash and from all
|
90
|
+
# nested hashes and arrays.
|
91
|
+
def deep_stringify_keys!
|
92
|
+
deep_transform_keys!(&:to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
96
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
97
|
+
# and from all nested hashes and arrays.
|
98
|
+
#
|
99
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
100
|
+
#
|
101
|
+
# hash.deep_symbolize_keys
|
102
|
+
# # => {:person=>{:name=>"Rob", :age=>"28"}}
|
103
|
+
def deep_symbolize_keys
|
104
|
+
deep_transform_keys { |key| key.to_sym rescue key }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Destructively converts all keys to symbols, as long as they respond
|
108
|
+
# to +to_sym+. This includes the keys from the root hash and from all
|
109
|
+
# nested hashes and arrays.
|
110
|
+
def deep_symbolize_keys!
|
111
|
+
deep_transform_keys! { |key| key.to_sym rescue key }
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
# support methods for deep transforming nested hashes and arrays
|
116
|
+
def _deep_transform_keys_in_object(object, &block)
|
117
|
+
case object
|
118
|
+
when Hash
|
119
|
+
object.each_with_object({}) do |(key, value), result|
|
120
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
121
|
+
end
|
122
|
+
when Array
|
123
|
+
object.map { |e| _deep_transform_keys_in_object(e, &block) }
|
124
|
+
else
|
125
|
+
object
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def _deep_transform_keys_in_object!(object, &block)
|
130
|
+
case object
|
131
|
+
when Hash
|
132
|
+
object.keys.each do |key|
|
133
|
+
value = object.delete(key)
|
134
|
+
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
|
135
|
+
end
|
136
|
+
object
|
137
|
+
when Array
|
138
|
+
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
|
139
|
+
else
|
140
|
+
object
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
|
+
#
|
6
|
+
# h1 = { a: true, b: { c: [1, 2, 3] } }
|
7
|
+
# h2 = { a: false, b: { x: [3, 4, 5] } }
|
8
|
+
#
|
9
|
+
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
|
10
|
+
#
|
11
|
+
# Like with Hash#merge in the standard library, a block can be provided
|
12
|
+
# to merge values:
|
13
|
+
#
|
14
|
+
# h1 = { a: 100, b: 200, c: { c1: 100 } }
|
15
|
+
# h2 = { b: 250, c: { c1: 200 } }
|
16
|
+
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
|
17
|
+
# # => { a: 100, b: 450, c: { c1: 300 } }
|
18
|
+
def deep_merge(other_hash, &block)
|
19
|
+
dup.deep_merge!(other_hash, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Same as +deep_merge+, but modifies +self+.
|
23
|
+
def deep_merge!(other_hash, &block)
|
24
|
+
merge!(other_hash) do |key, this_val, other_val|
|
25
|
+
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
26
|
+
this_val.deep_merge(other_val, &block)
|
27
|
+
elsif block_given?
|
28
|
+
block.call(key, this_val, other_val)
|
29
|
+
else
|
30
|
+
other_val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Merges the caller into +other_hash+. For example,
|
36
|
+
#
|
37
|
+
# options = options.reverse_merge(size: 25, velocity: 10)
|
38
|
+
#
|
39
|
+
# is equivalent to
|
40
|
+
#
|
41
|
+
# options = { size: 25, velocity: 10 }.merge(options)
|
42
|
+
#
|
43
|
+
# This is particularly useful for initializing an options hash
|
44
|
+
# with default values.
|
45
|
+
def reverse_merge(other_hash)
|
46
|
+
other_hash.merge(self)
|
47
|
+
end
|
48
|
+
alias_method :with_defaults, :reverse_merge
|
49
|
+
|
50
|
+
# Destructive +reverse_merge+.
|
51
|
+
def reverse_merge!(other_hash)
|
52
|
+
replace(reverse_merge(other_hash))
|
53
|
+
end
|
54
|
+
alias_method :reverse_update, :reverse_merge!
|
55
|
+
alias_method :with_defaults!, :reverse_merge!
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Replaces the hash with only the given keys.
|
5
|
+
# Returns a hash containing the removed key/value pairs.
|
6
|
+
#
|
7
|
+
# hash = { a: 1, b: 2, c: 3, d: 4 }
|
8
|
+
# hash.slice!(:a, :b) # => {:c=>3, :d=>4}
|
9
|
+
# hash # => {:a=>1, :b=>2}
|
10
|
+
def slice!(*keys)
|
11
|
+
omit = slice(*self.keys - keys)
|
12
|
+
hash = slice(*keys)
|
13
|
+
hash.default = default
|
14
|
+
hash.default_proc = default_proc if default_proc
|
15
|
+
replace(hash)
|
16
|
+
omit
|
17
|
+
end
|
18
|
+
|
19
|
+
# Removes and returns the key/value pairs matching the given keys.
|
20
|
+
#
|
21
|
+
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
|
22
|
+
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
|
23
|
+
def extract!(*keys)
|
24
|
+
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Returns a new hash with all values converted by the block operation.
|
5
|
+
# This includes the values from the root hash and from all
|
6
|
+
# nested hashes and arrays.
|
7
|
+
#
|
8
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
9
|
+
#
|
10
|
+
# hash.deep_transform_values{ |value| value.to_s.upcase }
|
11
|
+
# # => {person: {name: "ROB", age: "28"}}
|
12
|
+
def deep_transform_values(&block)
|
13
|
+
_deep_transform_values_in_object(self, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Destructively converts all values by using the block operation.
|
17
|
+
# This includes the values from the root hash and from all
|
18
|
+
# nested hashes and arrays.
|
19
|
+
def deep_transform_values!(&block)
|
20
|
+
_deep_transform_values_in_object!(self, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# support methods for deep transforming nested hashes and arrays
|
25
|
+
def _deep_transform_values_in_object(object, &block)
|
26
|
+
case object
|
27
|
+
when Hash
|
28
|
+
object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
|
29
|
+
when Array
|
30
|
+
object.map { |e| _deep_transform_values_in_object(e, &block) }
|
31
|
+
else
|
32
|
+
yield(object)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _deep_transform_values_in_object!(object, &block)
|
37
|
+
case object
|
38
|
+
when Hash
|
39
|
+
object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
|
40
|
+
when Array
|
41
|
+
object.map! { |e| _deep_transform_values_in_object!(e, &block) }
|
42
|
+
else
|
43
|
+
yield(object)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Numeric
|
4
|
+
KILOBYTE = 1024
|
5
|
+
MEGABYTE = KILOBYTE * 1024
|
6
|
+
GIGABYTE = MEGABYTE * 1024
|
7
|
+
TERABYTE = GIGABYTE * 1024
|
8
|
+
PETABYTE = TERABYTE * 1024
|
9
|
+
EXABYTE = PETABYTE * 1024
|
10
|
+
|
11
|
+
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
|
12
|
+
#
|
13
|
+
# 2.bytes # => 2
|
14
|
+
def bytes
|
15
|
+
self
|
16
|
+
end
|
17
|
+
alias :byte :bytes
|
18
|
+
|
19
|
+
# Returns the number of bytes equivalent to the kilobytes provided.
|
20
|
+
#
|
21
|
+
# 2.kilobytes # => 2048
|
22
|
+
def kilobytes
|
23
|
+
self * KILOBYTE
|
24
|
+
end
|
25
|
+
alias :kilobyte :kilobytes
|
26
|
+
|
27
|
+
# Returns the number of bytes equivalent to the megabytes provided.
|
28
|
+
#
|
29
|
+
# 2.megabytes # => 2_097_152
|
30
|
+
def megabytes
|
31
|
+
self * MEGABYTE
|
32
|
+
end
|
33
|
+
alias :megabyte :megabytes
|
34
|
+
|
35
|
+
# Returns the number of bytes equivalent to the gigabytes provided.
|
36
|
+
#
|
37
|
+
# 2.gigabytes # => 2_147_483_648
|
38
|
+
def gigabytes
|
39
|
+
self * GIGABYTE
|
40
|
+
end
|
41
|
+
alias :gigabyte :gigabytes
|
42
|
+
|
43
|
+
# Returns the number of bytes equivalent to the terabytes provided.
|
44
|
+
#
|
45
|
+
# 2.terabytes # => 2_199_023_255_552
|
46
|
+
def terabytes
|
47
|
+
self * TERABYTE
|
48
|
+
end
|
49
|
+
alias :terabyte :terabytes
|
50
|
+
|
51
|
+
# Returns the number of bytes equivalent to the petabytes provided.
|
52
|
+
#
|
53
|
+
# 2.petabytes # => 2_251_799_813_685_248
|
54
|
+
def petabytes
|
55
|
+
self * PETABYTE
|
56
|
+
end
|
57
|
+
alias :petabyte :petabytes
|
58
|
+
|
59
|
+
# Returns the number of bytes equivalent to the exabytes provided.
|
60
|
+
#
|
61
|
+
# 2.exabytes # => 2_305_843_009_213_693_952
|
62
|
+
def exabytes
|
63
|
+
self * EXABYTE
|
64
|
+
end
|
65
|
+
alias :exabyte :exabytes
|
66
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'concurrent/map'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
6
|
+
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
|
7
|
+
#
|
8
|
+
# This simplifies
|
9
|
+
#
|
10
|
+
# !address || address.empty?
|
11
|
+
#
|
12
|
+
# to
|
13
|
+
#
|
14
|
+
# address.blank?
|
15
|
+
#
|
16
|
+
# @return [true, false]
|
17
|
+
def blank?
|
18
|
+
respond_to?(:empty?) ? !!empty? : !self
|
19
|
+
end
|
20
|
+
|
21
|
+
# An object is present if it's not blank.
|
22
|
+
#
|
23
|
+
# @return [true, false]
|
24
|
+
def present?
|
25
|
+
!blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
29
|
+
# <tt>object.presence</tt> is equivalent to
|
30
|
+
#
|
31
|
+
# object.present? ? object : nil
|
32
|
+
#
|
33
|
+
# For example, something like
|
34
|
+
#
|
35
|
+
# state = params[:state] if params[:state].present?
|
36
|
+
# country = params[:country] if params[:country].present?
|
37
|
+
# region = state || country || 'US'
|
38
|
+
#
|
39
|
+
# becomes
|
40
|
+
#
|
41
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
def presence
|
45
|
+
self if present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NilClass
|
50
|
+
# +nil+ is blank:
|
51
|
+
#
|
52
|
+
# nil.blank? # => true
|
53
|
+
#
|
54
|
+
# @return [true]
|
55
|
+
def blank?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class FalseClass
|
61
|
+
# +false+ is blank:
|
62
|
+
#
|
63
|
+
# false.blank? # => true
|
64
|
+
#
|
65
|
+
# @return [true]
|
66
|
+
def blank?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class TrueClass
|
72
|
+
# +true+ is not blank:
|
73
|
+
#
|
74
|
+
# true.blank? # => false
|
75
|
+
#
|
76
|
+
# @return [false]
|
77
|
+
def blank?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Array
|
83
|
+
# An array is blank if it's empty:
|
84
|
+
#
|
85
|
+
# [].blank? # => true
|
86
|
+
# [1,2,3].blank? # => false
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
alias_method :blank?, :empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
class Hash
|
93
|
+
# A hash is blank if it's empty:
|
94
|
+
#
|
95
|
+
# {}.blank? # => true
|
96
|
+
# { key: 'value' }.blank? # => false
|
97
|
+
#
|
98
|
+
# @return [true, false]
|
99
|
+
alias_method :blank?, :empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
class String
|
103
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
104
|
+
ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
|
105
|
+
h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
|
106
|
+
end
|
107
|
+
|
108
|
+
# A string is blank if it's empty or contains whitespaces only:
|
109
|
+
#
|
110
|
+
# ''.blank? # => true
|
111
|
+
# ' '.blank? # => true
|
112
|
+
# "\t\n\r".blank? # => true
|
113
|
+
# ' blah '.blank? # => false
|
114
|
+
#
|
115
|
+
# Unicode whitespace is supported:
|
116
|
+
#
|
117
|
+
# "\u00a0".blank? # => true
|
118
|
+
#
|
119
|
+
# @return [true, false]
|
120
|
+
def blank?
|
121
|
+
# The regexp that matches blank strings is expensive. For the case of empty
|
122
|
+
# strings we can speed up this method (~3.5x) with an empty? call. The
|
123
|
+
# penalty for the rest of strings is marginal.
|
124
|
+
empty? ||
|
125
|
+
begin
|
126
|
+
BLANK_RE.match?(self)
|
127
|
+
rescue Encoding::CompatibilityError
|
128
|
+
ENCODED_BLANKS[self.encoding].match?(self)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Numeric #:nodoc:
|
134
|
+
# No number is blank:
|
135
|
+
#
|
136
|
+
# 1.blank? # => false
|
137
|
+
# 0.blank? # => false
|
138
|
+
#
|
139
|
+
# @return [false]
|
140
|
+
def blank?
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Time #:nodoc:
|
146
|
+
# No Time is blank:
|
147
|
+
#
|
148
|
+
# Time.now.blank? # => false
|
149
|
+
#
|
150
|
+
# @return [false]
|
151
|
+
def blank?
|
152
|
+
false
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# Returns true if this object is included in the argument. Argument must be
|
5
|
+
# any object which responds to +#include?+. Usage:
|
6
|
+
#
|
7
|
+
# characters = ["Konata", "Kagami", "Tsukasa"]
|
8
|
+
# "Konata".in?(characters) # => true
|
9
|
+
#
|
10
|
+
# This will throw an +ArgumentError+ if the argument doesn't respond
|
11
|
+
# to +#include?+.
|
12
|
+
def in?(another_object)
|
13
|
+
another_object.include?(self)
|
14
|
+
rescue NoMethodError
|
15
|
+
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the receiver if it's included in the argument otherwise returns +nil+.
|
19
|
+
# Argument must be any object which responds to +#include?+. Usage:
|
20
|
+
#
|
21
|
+
# params[:bucket_type].presence_in %w( project calendar )
|
22
|
+
#
|
23
|
+
# This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+.
|
24
|
+
#
|
25
|
+
# @return [Object]
|
26
|
+
def presence_in(another_object)
|
27
|
+
in?(another_object) ? self : nil
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# Returns a hash with string keys that maps instance variable names without "@" to their
|
5
|
+
# corresponding values.
|
6
|
+
#
|
7
|
+
# class C
|
8
|
+
# def initialize(x, y)
|
9
|
+
# @x, @y = x, y
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
|
14
|
+
def instance_values
|
15
|
+
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an array of instance variable names as strings including "@".
|
19
|
+
#
|
20
|
+
# class C
|
21
|
+
# def initialize(x, y)
|
22
|
+
# @x, @y = x, y
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
|
27
|
+
def instance_variable_names
|
28
|
+
instance_variables.map(&:to_s)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
class Object
|
6
|
+
# Alias of <tt>to_s</tt>.
|
7
|
+
def to_param
|
8
|
+
to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
# Converts an object into a string suitable for use as a URL query string,
|
12
|
+
# using the given <tt>key</tt> as the param name.
|
13
|
+
def to_query(key)
|
14
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class NilClass
|
19
|
+
# Returns +self+.
|
20
|
+
def to_param
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class TrueClass
|
26
|
+
# Returns +self+.
|
27
|
+
def to_param
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class FalseClass
|
33
|
+
# Returns +self+.
|
34
|
+
def to_param
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Array
|
40
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
41
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
42
|
+
def to_param
|
43
|
+
collect(&:to_param).join "/"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts an array into a string suitable for use as a URL query string,
|
47
|
+
# using the given +key+ as the param name.
|
48
|
+
#
|
49
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
50
|
+
def to_query(key)
|
51
|
+
prefix = "#{key}[]"
|
52
|
+
|
53
|
+
if empty?
|
54
|
+
nil.to_query(prefix)
|
55
|
+
else
|
56
|
+
collect { |value| value.to_query(prefix) }.join "&"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Hash
|
62
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
63
|
+
# query string:
|
64
|
+
#
|
65
|
+
# {name: 'David', nationality: 'Danish'}.to_query
|
66
|
+
# # => "name=David&nationality=Danish"
|
67
|
+
#
|
68
|
+
# An optional namespace can be passed to enclose key names:
|
69
|
+
#
|
70
|
+
# {name: 'David', nationality: 'Danish'}.to_query('user')
|
71
|
+
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
|
72
|
+
#
|
73
|
+
# The string pairs "key=value" that conform the query string
|
74
|
+
# are sorted lexicographically in ascending order.
|
75
|
+
#
|
76
|
+
# This method is also aliased as +to_param+.
|
77
|
+
def to_query(namespace = nil)
|
78
|
+
query = collect do |key, value|
|
79
|
+
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
|
80
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
81
|
+
end
|
82
|
+
end.compact
|
83
|
+
|
84
|
+
query.sort! unless namespace.to_s.include?("[]")
|
85
|
+
query.join("&")
|
86
|
+
end
|
87
|
+
|
88
|
+
alias_method :to_param, :to_query
|
89
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
##
|
5
|
+
# :method: try
|
6
|
+
#
|
7
|
+
# :call-seq:
|
8
|
+
# try(*a, &b)
|
9
|
+
#
|
10
|
+
# Invokes the public method whose name goes as first argument just like
|
11
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
12
|
+
# call returns +nil+ rather than raising an exception.
|
13
|
+
#
|
14
|
+
# This method is defined to be able to write
|
15
|
+
#
|
16
|
+
# @person.try(:name)
|
17
|
+
#
|
18
|
+
# instead of
|
19
|
+
#
|
20
|
+
# @person.name if @person
|
21
|
+
#
|
22
|
+
# +try+ calls can be chained:
|
23
|
+
#
|
24
|
+
# @person.try(:spouse).try(:name)
|
25
|
+
#
|
26
|
+
# instead of
|
27
|
+
#
|
28
|
+
# @person.spouse.name if @person && @person.spouse
|
29
|
+
#
|
30
|
+
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
31
|
+
#
|
32
|
+
# @person.try(:non_existing_method) # => nil
|
33
|
+
#
|
34
|
+
# instead of
|
35
|
+
#
|
36
|
+
# @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
|
37
|
+
#
|
38
|
+
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
39
|
+
# to the method:
|
40
|
+
#
|
41
|
+
# nil.try(:to_i) # => nil, rather than 0
|
42
|
+
#
|
43
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
44
|
+
#
|
45
|
+
# @posts.try(:each_slice, 2) do |a, b|
|
46
|
+
# ...
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# The number of arguments in the signature must match. If the object responds
|
50
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
51
|
+
# in case of argument mismatch.
|
52
|
+
#
|
53
|
+
# If +try+ is called without arguments it yields the receiver to a given
|
54
|
+
# block unless it is +nil+:
|
55
|
+
#
|
56
|
+
# @person.try do |p|
|
57
|
+
# ...
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# You can also call try with a block without accepting an argument, and the block
|
61
|
+
# will be instance_eval'ed instead:
|
62
|
+
#
|
63
|
+
# @person.try { upcase.truncate(50) }
|
64
|
+
#
|
65
|
+
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
66
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
67
|
+
# like direct subclasses of +BasicObject+.
|
68
|
+
|
69
|
+
##
|
70
|
+
# :method: try!
|
71
|
+
#
|
72
|
+
# :call-seq:
|
73
|
+
# try!(*a, &b)
|
74
|
+
#
|
75
|
+
# Same as #try, but raises a +NoMethodError+ exception if the receiver is
|
76
|
+
# not +nil+ and does not implement the tried method.
|
77
|
+
#
|
78
|
+
# "a".try!(:upcase) # => "A"
|
79
|
+
# nil.try!(:upcase) # => nil
|
80
|
+
# 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
|
81
|
+
def try(method_name = nil, *args, &b)
|
82
|
+
if method_name.nil? && block_given?
|
83
|
+
if b.arity == 0
|
84
|
+
instance_eval(&b)
|
85
|
+
else
|
86
|
+
yield self
|
87
|
+
end
|
88
|
+
elsif respond_to?(method_name)
|
89
|
+
public_send(method_name, *args, &b)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def try!(method_name = nil, *args, &b)
|
94
|
+
if method_name.nil? && block_given?
|
95
|
+
if b.arity == 0
|
96
|
+
instance_eval(&b)
|
97
|
+
else
|
98
|
+
yield self
|
99
|
+
end
|
100
|
+
else
|
101
|
+
public_send(method_name, *args, &b)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class NilClass
|
107
|
+
# Calling +try+ on +nil+ always returns +nil+.
|
108
|
+
# It becomes especially helpful when navigating through associations that may return +nil+.
|
109
|
+
#
|
110
|
+
# nil.try(:name) # => nil
|
111
|
+
#
|
112
|
+
# Without +try+
|
113
|
+
# @person && @person.children.any? && @person.children.first.name
|
114
|
+
#
|
115
|
+
# With +try+
|
116
|
+
# @person.try(:children).try(:first).try(:name)
|
117
|
+
def try(method_name = nil, *args)
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
# Calling +try!+ on +nil+ always returns +nil+.
|
122
|
+
#
|
123
|
+
# nil.try!(:name) # => nil
|
124
|
+
def try!(method_name = nil, *args)
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
end
|
data/lib/simple_ext.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_ext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tushar Hawaldar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Core ruby extentions extracted from Rails ActiveSupport
|
14
|
+
email:
|
15
|
+
- kumartushar1111@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/simple_ext.rb
|
21
|
+
- lib/simple_ext/array.rb
|
22
|
+
- lib/simple_ext/array/access.rb
|
23
|
+
- lib/simple_ext/digest.rb
|
24
|
+
- lib/simple_ext/digest/uuid.rb
|
25
|
+
- lib/simple_ext/file.rb
|
26
|
+
- lib/simple_ext/file/atomic.rb
|
27
|
+
- lib/simple_ext/hash.rb
|
28
|
+
- lib/simple_ext/hash/except.rb
|
29
|
+
- lib/simple_ext/hash/keys.rb
|
30
|
+
- lib/simple_ext/hash/merge.rb
|
31
|
+
- lib/simple_ext/hash/slice.rb
|
32
|
+
- lib/simple_ext/hash/values.rb
|
33
|
+
- lib/simple_ext/numeric.rb
|
34
|
+
- lib/simple_ext/numeric/bytes.rb
|
35
|
+
- lib/simple_ext/object.rb
|
36
|
+
- lib/simple_ext/object/blank.rb
|
37
|
+
- lib/simple_ext/object/inclusion.rb
|
38
|
+
- lib/simple_ext/object/instance_variables.rb
|
39
|
+
- lib/simple_ext/object/to_query.rb
|
40
|
+
- lib/simple_ext/object/try.rb
|
41
|
+
homepage: https://github.com/kumartushar/simple_ext.git
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubygems_version: 3.1.2
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Simple Ruby Extentions
|
64
|
+
test_files: []
|