sigterm_extensions 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +17 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/bin/ctxirb +156 -0
  7. data/lib/git.rb +166 -0
  8. data/lib/git/LICENSE +21 -0
  9. data/lib/git/author.rb +14 -0
  10. data/lib/git/base.rb +551 -0
  11. data/lib/git/base/factory.rb +75 -0
  12. data/lib/git/branch.rb +126 -0
  13. data/lib/git/branches.rb +71 -0
  14. data/lib/git/config.rb +22 -0
  15. data/lib/git/diff.rb +159 -0
  16. data/lib/git/index.rb +5 -0
  17. data/lib/git/lib.rb +1041 -0
  18. data/lib/git/log.rb +128 -0
  19. data/lib/git/object.rb +312 -0
  20. data/lib/git/path.rb +31 -0
  21. data/lib/git/remote.rb +36 -0
  22. data/lib/git/repository.rb +6 -0
  23. data/lib/git/stash.rb +27 -0
  24. data/lib/git/stashes.rb +55 -0
  25. data/lib/git/status.rb +199 -0
  26. data/lib/git/version.rb +5 -0
  27. data/lib/git/working_directory.rb +4 -0
  28. data/lib/sigterm_extensions.rb +75 -0
  29. data/lib/sigterm_extensions/all.rb +12 -0
  30. data/lib/sigterm_extensions/backtrace_cleaner.rb +129 -0
  31. data/lib/sigterm_extensions/callbacks.rb +847 -0
  32. data/lib/sigterm_extensions/concern.rb +169 -0
  33. data/lib/sigterm_extensions/configurable.rb +38 -0
  34. data/lib/sigterm_extensions/core_ext.rb +4 -0
  35. data/lib/sigterm_extensions/core_ext/array.rb +3 -0
  36. data/lib/sigterm_extensions/core_ext/array/extract.rb +19 -0
  37. data/lib/sigterm_extensions/core_ext/array/extract_options.rb +29 -0
  38. data/lib/sigterm_extensions/core_ext/class.rb +3 -0
  39. data/lib/sigterm_extensions/core_ext/class/attribute.rb +139 -0
  40. data/lib/sigterm_extensions/core_ext/class/attribute_accessors.rb +4 -0
  41. data/lib/sigterm_extensions/core_ext/class/subclasses.rb +52 -0
  42. data/lib/sigterm_extensions/core_ext/custom.rb +12 -0
  43. data/lib/sigterm_extensions/core_ext/digest.rb +3 -0
  44. data/lib/sigterm_extensions/core_ext/digest/uuid.rb +51 -0
  45. data/lib/sigterm_extensions/core_ext/enumerable.rb +232 -0
  46. data/lib/sigterm_extensions/core_ext/file.rb +3 -0
  47. data/lib/sigterm_extensions/core_ext/file/atomic.rb +68 -0
  48. data/lib/sigterm_extensions/core_ext/hash.rb +3 -0
  49. data/lib/sigterm_extensions/core_ext/hash/deep_merge.rb +41 -0
  50. data/lib/sigterm_extensions/core_ext/hash/deep_transform_values.rb +44 -0
  51. data/lib/sigterm_extensions/core_ext/hash/except.rb +22 -0
  52. data/lib/sigterm_extensions/core_ext/hash/keys.rb +141 -0
  53. data/lib/sigterm_extensions/core_ext/hash/reverse_merge.rb +23 -0
  54. data/lib/sigterm_extensions/core_ext/hash/slice.rb +24 -0
  55. data/lib/sigterm_extensions/core_ext/kernel.rb +3 -0
  56. data/lib/sigterm_extensions/core_ext/kernel/concern.rb +12 -0
  57. data/lib/sigterm_extensions/core_ext/kernel/reporting.rb +43 -0
  58. data/lib/sigterm_extensions/core_ext/kernel/singleton_class.rb +6 -0
  59. data/lib/sigterm_extensions/core_ext/load_error.rb +7 -0
  60. data/lib/sigterm_extensions/core_ext/module.rb +3 -0
  61. data/lib/sigterm_extensions/core_ext/module/aliasing.rb +29 -0
  62. data/lib/sigterm_extensions/core_ext/module/anonymous.rb +28 -0
  63. data/lib/sigterm_extensions/core_ext/module/attr_internal.rb +36 -0
  64. data/lib/sigterm_extensions/core_ext/module/attribute_accessors.rb +208 -0
  65. data/lib/sigterm_extensions/core_ext/module/attribute_accessors_per_thread.rb +146 -0
  66. data/lib/sigterm_extensions/core_ext/module/concerning.rb +132 -0
  67. data/lib/sigterm_extensions/core_ext/module/delegation.rb +319 -0
  68. data/lib/sigterm_extensions/core_ext/module/redefine_method.rb +38 -0
  69. data/lib/sigterm_extensions/core_ext/module/remove_method.rb +15 -0
  70. data/lib/sigterm_extensions/core_ext/name_error.rb +36 -0
  71. data/lib/sigterm_extensions/core_ext/object.rb +3 -0
  72. data/lib/sigterm_extensions/core_ext/object/blank.rb +153 -0
  73. data/lib/sigterm_extensions/core_ext/object/colors.rb +39 -0
  74. data/lib/sigterm_extensions/core_ext/object/duplicable.rb +47 -0
  75. data/lib/sigterm_extensions/core_ext/object/inclusion.rb +27 -0
  76. data/lib/sigterm_extensions/core_ext/object/instance_variables.rb +28 -0
  77. data/lib/sigterm_extensions/core_ext/object/methods.rb +61 -0
  78. data/lib/sigterm_extensions/core_ext/object/with_options.rb +80 -0
  79. data/lib/sigterm_extensions/core_ext/range.rb +3 -0
  80. data/lib/sigterm_extensions/core_ext/range/compare_range.rb +74 -0
  81. data/lib/sigterm_extensions/core_ext/range/conversions.rb +39 -0
  82. data/lib/sigterm_extensions/core_ext/range/overlaps.rb +8 -0
  83. data/lib/sigterm_extensions/core_ext/securerandom.rb +43 -0
  84. data/lib/sigterm_extensions/core_ext/string.rb +3 -0
  85. data/lib/sigterm_extensions/core_ext/string/access.rb +93 -0
  86. data/lib/sigterm_extensions/core_ext/string/filters.rb +143 -0
  87. data/lib/sigterm_extensions/core_ext/string/starts_ends_with.rb +4 -0
  88. data/lib/sigterm_extensions/core_ext/string/strip.rb +25 -0
  89. data/lib/sigterm_extensions/core_ext/tryable.rb +132 -0
  90. data/lib/sigterm_extensions/descendants_tracker.rb +108 -0
  91. data/lib/sigterm_extensions/gem_methods.rb +47 -0
  92. data/lib/sigterm_extensions/hash_binding.rb +16 -0
  93. data/lib/sigterm_extensions/inflector.rb +339 -0
  94. data/lib/sigterm_extensions/inflector/acronyms.rb +42 -0
  95. data/lib/sigterm_extensions/inflector/inflections.rb +249 -0
  96. data/lib/sigterm_extensions/inflector/inflections/defaults.rb +117 -0
  97. data/lib/sigterm_extensions/inflector/rules.rb +37 -0
  98. data/lib/sigterm_extensions/inflector/version.rb +8 -0
  99. data/lib/sigterm_extensions/interactive_editor.rb +120 -0
  100. data/lib/sigterm_extensions/lazy.rb +34 -0
  101. data/lib/sigterm_extensions/lazy_load_hooks.rb +79 -0
  102. data/lib/sigterm_extensions/option_merger.rb +32 -0
  103. data/lib/sigterm_extensions/ordered_hash.rb +48 -0
  104. data/lib/sigterm_extensions/ordered_options.rb +83 -0
  105. data/lib/sigterm_extensions/paths.rb +235 -0
  106. data/lib/sigterm_extensions/per_thread_registry.rb +58 -0
  107. data/lib/sigterm_extensions/proxy_object.rb +14 -0
  108. data/lib/sigterm_extensions/staging/boot.rb +31 -0
  109. data/lib/sigterm_extensions/staging/boot/bundler_patch.rb +24 -0
  110. data/lib/sigterm_extensions/staging/boot/command.rb +26 -0
  111. data/lib/sigterm_extensions/staging/boot/gemfile_next_auto_sync.rb +79 -0
  112. data/lib/sigterm_extensions/version.rb +4 -0
  113. data/lib/sigterm_extensions/wrappable.rb +16 -0
  114. data/sigterm_extensions.gemspec +42 -0
  115. data/templates/dotpryrc.rb.erb +124 -0
  116. metadata +315 -0
@@ -0,0 +1,12 @@
1
+ class SafeObject
2
+
3
+ def method_missing(name, *args, &blk)
4
+ puts "method_missing => #{name}, #{args}"
5
+ end
6
+
7
+ def respond_to_missing?(name, include_private = false)
8
+ puts "respond_to_missing? => #{name}, #{include_private}"
9
+ true
10
+ end
11
+
12
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path('digest/*.rb', __dir__)).each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,51 @@
1
+ require "securerandom"
2
+
3
+ module Digest
4
+ module UUID
5
+ DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
6
+ URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
7
+ OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
8
+ X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
9
+
10
+ # Generates a v5 non-random UUID (Universally Unique IDentifier).
11
+ #
12
+ # Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
13
+ # uuid_from_hash always generates the same UUID for a given name and namespace combination.
14
+ #
15
+ # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
16
+ def self.uuid_from_hash(hash_class, uuid_namespace, name)
17
+ if hash_class == Digest::MD5
18
+ version = 3
19
+ elsif hash_class == Digest::SHA1
20
+ version = 5
21
+ else
22
+ raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
23
+ end
24
+
25
+ hash = hash_class.new
26
+ hash.update(uuid_namespace)
27
+ hash.update(name)
28
+
29
+ ary = hash.digest.unpack("NnnnnN")
30
+ ary[2] = (ary[2] & 0x0FFF) | (version << 12)
31
+ ary[3] = (ary[3] & 0x3FFF) | 0x8000
32
+
33
+ "%08x-%04x-%04x-%04x-%04x%08x" % ary
34
+ end
35
+
36
+ # Convenience method for uuid_from_hash using Digest::MD5.
37
+ def self.uuid_v3(uuid_namespace, name)
38
+ uuid_from_hash(Digest::MD5, uuid_namespace, name)
39
+ end
40
+
41
+ # Convenience method for uuid_from_hash using Digest::SHA1.
42
+ def self.uuid_v5(uuid_namespace, name)
43
+ uuid_from_hash(Digest::SHA1, uuid_namespace, name)
44
+ end
45
+
46
+ # Convenience method for SecureRandom.uuid.
47
+ def self.uuid_v4
48
+ SecureRandom.uuid
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,232 @@
1
+ module Enumerable
2
+ INDEX_WITH_DEFAULT = Object.new
3
+ private_constant :INDEX_WITH_DEFAULT
4
+
5
+ # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
6
+ # when we omit an identity.
7
+
8
+ # :stopdoc:
9
+
10
+ # We can't use Refinements here because Refinements with Module which will be prepended
11
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
12
+ alias :_original_sum_with_required_identity :sum
13
+ private :_original_sum_with_required_identity
14
+
15
+ # :startdoc:
16
+
17
+ # Calculates a sum from the elements.
18
+ #
19
+ # payments.sum { |p| p.price * p.tax_rate }
20
+ # payments.sum(&:price)
21
+ #
22
+ # The latter is a shortcut for:
23
+ #
24
+ # payments.inject(0) { |sum, p| sum + p.price }
25
+ #
26
+ # It can also calculate the sum without the use of a block.
27
+ #
28
+ # [5, 15, 10].sum # => 30
29
+ # ['foo', 'bar'].sum # => "foobar"
30
+ # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
31
+ #
32
+ # The default sum of an empty list is zero. You can override this default:
33
+ #
34
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
35
+ def sum(identity = nil, &block)
36
+ if identity
37
+ _original_sum_with_required_identity(identity, &block)
38
+ elsif block_given?
39
+ map(&block).sum(identity)
40
+ else
41
+ inject(:+) || 0
42
+ end
43
+ end
44
+
45
+ # Convert an enumerable to a hash keying it by the block return value.
46
+ #
47
+ # people.index_by(&:login)
48
+ # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
49
+ #
50
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
51
+ # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
52
+ def index_by
53
+ if block_given?
54
+ result = {}
55
+ each { |elem| result[yield(elem)] = elem }
56
+ result
57
+ else
58
+ to_enum(:index_by) { size if respond_to?(:size) }
59
+ end
60
+ end
61
+
62
+ # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
63
+ #
64
+ # post = Post.new(title: "hey there", body: "what's up?")
65
+ #
66
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
67
+ # # => { title: "hey there", body: "what's up?" }
68
+ def index_with(default = INDEX_WITH_DEFAULT)
69
+ if block_given?
70
+ result = {}
71
+ each { |elem| result[elem] = yield(elem) }
72
+ result
73
+ elsif default != INDEX_WITH_DEFAULT
74
+ result = {}
75
+ each { |elem| result[elem] = default }
76
+ result
77
+ else
78
+ to_enum(:index_with) { size if respond_to?(:size) }
79
+ end
80
+ end
81
+
82
+ # Returns +true+ if the enumerable has more than 1 element. Functionally
83
+ # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
84
+ # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
85
+ # if more than one person is over 26.
86
+ def many?
87
+ cnt = 0
88
+ if block_given?
89
+ any? do |element|
90
+ cnt += 1 if yield element
91
+ cnt > 1
92
+ end
93
+ else
94
+ any? { (cnt += 1) > 1 }
95
+ end
96
+ end
97
+
98
+ # Returns a new array that includes the passed elements.
99
+ #
100
+ # [ 1, 2, 3 ].including(4, 5)
101
+ # # => [ 1, 2, 3, 4, 5 ]
102
+ #
103
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
104
+ # # => ["David", "Rafael", "Aaron", "Todd"]
105
+ def including(*elements)
106
+ to_a.including(*elements)
107
+ end
108
+
109
+ # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
110
+ # collection does not include the object.
111
+ def exclude?(object)
112
+ !include?(object)
113
+ end
114
+
115
+ # Returns a copy of the enumerable excluding the specified elements.
116
+ #
117
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
118
+ # # => ["David", "Rafael"]
119
+ #
120
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
121
+ # # => ["David", "Rafael"]
122
+ #
123
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
124
+ # # => {foo: 1, baz: 3}
125
+ def excluding(*elements)
126
+ elements.flatten!(1)
127
+ reject { |element| elements.include?(element) }
128
+ end
129
+
130
+ # Alias for #excluding.
131
+ def without(*elements)
132
+ excluding(*elements)
133
+ end
134
+
135
+ # Convert an enumerable to an array based on the given key.
136
+ #
137
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
138
+ # # => ["David", "Rafael", "Aaron"]
139
+ #
140
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
141
+ # # => [[1, "David"], [2, "Rafael"]]
142
+ def pluck(*keys)
143
+ if keys.many?
144
+ map { |element| keys.map { |key| element[key] } }
145
+ else
146
+ map { |element| element[keys.first] }
147
+ end
148
+ end
149
+
150
+ # Returns a new +Array+ without the blank items.
151
+ # Uses Object#blank? for determining if an item is blank.
152
+ #
153
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
154
+ # # => [1, 2, true]
155
+ #
156
+ # Set.new([nil, "", 1, 2])
157
+ # # => [2, 1] (or [1, 2])
158
+ #
159
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
160
+ #
161
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
162
+ # #=> { b: 1, f: true }
163
+ def compact_blank
164
+ reject(&:blank?)
165
+ end
166
+ end
167
+
168
+ class Hash
169
+ # Hash#reject has its own definition, so this needs one too.
170
+ def compact_blank #:nodoc:
171
+ reject { |_k, v| v.blank? }
172
+ end
173
+
174
+ # Removes all blank values from the +Hash+ in place and returns self.
175
+ # Uses Object#blank? for determining if a value is blank.
176
+ #
177
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
178
+ # h.compact_blank!
179
+ # # => { b: 1, f: true }
180
+ def compact_blank!
181
+ # use delete_if rather than reject! because it always returns self even if nothing changed
182
+ delete_if { |_k, v| v.blank? }
183
+ end
184
+ end
185
+
186
+ class Range #:nodoc:
187
+ # Optimize range sum to use arithmetic progression if a block is not given and
188
+ # we have a range of numeric values.
189
+ def sum(identity = nil)
190
+ if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
191
+ super
192
+ else
193
+ actual_last = exclude_end? ? (last - 1) : last
194
+ if actual_last >= first
195
+ sum = identity || 0
196
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
197
+ else
198
+ identity || 0
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Using Refinements here in order not to expose our internal method
205
+ using Module.new {
206
+ refine Array do
207
+ alias :orig_sum :sum
208
+ end
209
+ }
210
+
211
+ class Array #:nodoc:
212
+ # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
213
+ def sum(init = nil, &block)
214
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
215
+ init ||= 0
216
+ orig_sum(init, &block)
217
+ else
218
+ super
219
+ end
220
+ end
221
+
222
+ # Removes all blank elements from the +Array+ in place and returns self.
223
+ # Uses Object#blank? for determining if an item is blank.
224
+ #
225
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
226
+ # a.compact_blank!
227
+ # # => [1, 2, true]
228
+ def compact_blank!
229
+ # use delete_if rather than reject! because it always returns self even if nothing changed
230
+ delete_if(&:blank?)
231
+ end
232
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path('file/*.rb', __dir__)).each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,68 @@
1
+ require "fileutils"
2
+
3
+ class File
4
+ # Write to a file atomically. Useful for situations where you don't
5
+ # want other processes or threads to see half-written files.
6
+ #
7
+ # File.atomic_write('important.file') do |file|
8
+ # file.write('hello')
9
+ # end
10
+ #
11
+ # This method needs to create a temporary file. By default it will create it
12
+ # in the same directory as the destination file. If you don't like this
13
+ # behavior you can provide a different directory but it must be on the
14
+ # same physical filesystem as the file you're trying to write.
15
+ #
16
+ # File.atomic_write('/data/something.important', '/data/tmp') do |file|
17
+ # file.write('hello')
18
+ # end
19
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
20
+ require "tempfile" unless defined?(Tempfile)
21
+
22
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
23
+ temp_file.binmode
24
+ return_val = yield temp_file
25
+ temp_file.close
26
+
27
+ old_stat = if exist?(file_name)
28
+ # Get original file permissions
29
+ stat(file_name)
30
+ else
31
+ # If not possible, probe which are the default permissions in the
32
+ # destination directory.
33
+ probe_stat_in(dirname(file_name))
34
+ end
35
+
36
+ if old_stat
37
+ # Set correct permissions on new file
38
+ begin
39
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
40
+ # This operation will affect filesystem ACL's
41
+ chmod(old_stat.mode, temp_file.path)
42
+ rescue Errno::EPERM, Errno::EACCES
43
+ # Changing file ownership failed, moving on.
44
+ end
45
+ end
46
+
47
+ # Overwrite original file with temp file
48
+ rename(temp_file.path, file_name)
49
+ return_val
50
+ end
51
+ end
52
+
53
+ # Private utility method.
54
+ def self.probe_stat_in(dir) #:nodoc:
55
+ basename = [
56
+ ".permissions_check",
57
+ Thread.current.object_id,
58
+ Process.pid,
59
+ rand(1000000)
60
+ ].join(".")
61
+
62
+ file_name = join(dir, basename)
63
+ FileUtils.touch(file_name)
64
+ stat(file_name)
65
+ ensure
66
+ FileUtils.rm_f(file_name) if file_name
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path('hash/*.rb', __dir__)).each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,41 @@
1
+ class Hash
2
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
+ #
4
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
5
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
6
+ #
7
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
8
+ #
9
+ # Like with Hash#merge in the standard library, a block can be provided
10
+ # to merge values:
11
+ #
12
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
13
+ # h2 = { b: 250, c: { c1: 200 } }
14
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
15
+ # # => { a: 100, b: 450, c: { c1: 300 } }
16
+ def deep_merge(other_hash, &block)
17
+ dup.deep_merge!(other_hash, &block)
18
+ end
19
+
20
+ # Same as +deep_merge+, but modifies +self+.
21
+ def deep_merge!(other_hash, &block)
22
+ merge!(other_hash) do |key, this_val, other_val|
23
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
24
+ this_val.deep_merge(other_val, &block)
25
+ elsif block_given?
26
+ block.call(key, this_val, other_val)
27
+ else
28
+ other_val
29
+ end
30
+ end
31
+ end
32
+
33
+ #def deep_merge(second)
34
+ # merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
35
+ # self.merge(second.to_h, &merger)
36
+ #end
37
+ #def method_missing(name, *args)
38
+ # send(:[], name.to_s, *args)
39
+ #end
40
+ end
41
+
@@ -0,0 +1,44 @@
1
+ class Hash
2
+ # Returns a new hash with all values converted by the block operation.
3
+ # This includes the values from the root hash and from all
4
+ # nested hashes and arrays.
5
+ #
6
+ # hash = { person: { name: 'Rob', age: '28' } }
7
+ #
8
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
9
+ # # => {person: {name: "ROB", age: "28"}}
10
+ def deep_transform_values(&block)
11
+ _deep_transform_values_in_object(self, &block)
12
+ end
13
+
14
+ # Destructively converts all values by using the block operation.
15
+ # This includes the values from the root hash and from all
16
+ # nested hashes and arrays.
17
+ def deep_transform_values!(&block)
18
+ _deep_transform_values_in_object!(self, &block)
19
+ end
20
+
21
+ private
22
+ # support methods for deep transforming nested hashes and arrays
23
+ def _deep_transform_values_in_object(object, &block)
24
+ case object
25
+ when Hash
26
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
27
+ when Array
28
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
29
+ else
30
+ yield(object)
31
+ end
32
+ end
33
+
34
+ def _deep_transform_values_in_object!(object, &block)
35
+ case object
36
+ when Hash
37
+ object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
38
+ when Array
39
+ object.map! { |e| _deep_transform_values_in_object!(e, &block) }
40
+ else
41
+ yield(object)
42
+ end
43
+ end
44
+ end