sigterm_extensions 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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