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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +6 -0
- data/LICENSE.md +0 -0
- data/README.md +0 -0
- data/bin/ctxirb +156 -0
- data/lib/git.rb +166 -0
- data/lib/git/LICENSE +21 -0
- data/lib/git/author.rb +14 -0
- data/lib/git/base.rb +551 -0
- data/lib/git/base/factory.rb +75 -0
- data/lib/git/branch.rb +126 -0
- data/lib/git/branches.rb +71 -0
- data/lib/git/config.rb +22 -0
- data/lib/git/diff.rb +159 -0
- data/lib/git/index.rb +5 -0
- data/lib/git/lib.rb +1041 -0
- data/lib/git/log.rb +128 -0
- data/lib/git/object.rb +312 -0
- data/lib/git/path.rb +31 -0
- data/lib/git/remote.rb +36 -0
- data/lib/git/repository.rb +6 -0
- data/lib/git/stash.rb +27 -0
- data/lib/git/stashes.rb +55 -0
- data/lib/git/status.rb +199 -0
- data/lib/git/version.rb +5 -0
- data/lib/git/working_directory.rb +4 -0
- data/lib/sigterm_extensions.rb +75 -0
- data/lib/sigterm_extensions/all.rb +12 -0
- data/lib/sigterm_extensions/backtrace_cleaner.rb +129 -0
- data/lib/sigterm_extensions/callbacks.rb +847 -0
- data/lib/sigterm_extensions/concern.rb +169 -0
- data/lib/sigterm_extensions/configurable.rb +38 -0
- data/lib/sigterm_extensions/core_ext.rb +4 -0
- data/lib/sigterm_extensions/core_ext/array.rb +3 -0
- data/lib/sigterm_extensions/core_ext/array/extract.rb +19 -0
- data/lib/sigterm_extensions/core_ext/array/extract_options.rb +29 -0
- data/lib/sigterm_extensions/core_ext/class.rb +3 -0
- data/lib/sigterm_extensions/core_ext/class/attribute.rb +139 -0
- data/lib/sigterm_extensions/core_ext/class/attribute_accessors.rb +4 -0
- data/lib/sigterm_extensions/core_ext/class/subclasses.rb +52 -0
- data/lib/sigterm_extensions/core_ext/custom.rb +12 -0
- data/lib/sigterm_extensions/core_ext/digest.rb +3 -0
- data/lib/sigterm_extensions/core_ext/digest/uuid.rb +51 -0
- data/lib/sigterm_extensions/core_ext/enumerable.rb +232 -0
- data/lib/sigterm_extensions/core_ext/file.rb +3 -0
- data/lib/sigterm_extensions/core_ext/file/atomic.rb +68 -0
- data/lib/sigterm_extensions/core_ext/hash.rb +3 -0
- data/lib/sigterm_extensions/core_ext/hash/deep_merge.rb +41 -0
- data/lib/sigterm_extensions/core_ext/hash/deep_transform_values.rb +44 -0
- data/lib/sigterm_extensions/core_ext/hash/except.rb +22 -0
- data/lib/sigterm_extensions/core_ext/hash/keys.rb +141 -0
- data/lib/sigterm_extensions/core_ext/hash/reverse_merge.rb +23 -0
- data/lib/sigterm_extensions/core_ext/hash/slice.rb +24 -0
- data/lib/sigterm_extensions/core_ext/kernel.rb +3 -0
- data/lib/sigterm_extensions/core_ext/kernel/concern.rb +12 -0
- data/lib/sigterm_extensions/core_ext/kernel/reporting.rb +43 -0
- data/lib/sigterm_extensions/core_ext/kernel/singleton_class.rb +6 -0
- data/lib/sigterm_extensions/core_ext/load_error.rb +7 -0
- data/lib/sigterm_extensions/core_ext/module.rb +3 -0
- data/lib/sigterm_extensions/core_ext/module/aliasing.rb +29 -0
- data/lib/sigterm_extensions/core_ext/module/anonymous.rb +28 -0
- data/lib/sigterm_extensions/core_ext/module/attr_internal.rb +36 -0
- data/lib/sigterm_extensions/core_ext/module/attribute_accessors.rb +208 -0
- data/lib/sigterm_extensions/core_ext/module/attribute_accessors_per_thread.rb +146 -0
- data/lib/sigterm_extensions/core_ext/module/concerning.rb +132 -0
- data/lib/sigterm_extensions/core_ext/module/delegation.rb +319 -0
- data/lib/sigterm_extensions/core_ext/module/redefine_method.rb +38 -0
- data/lib/sigterm_extensions/core_ext/module/remove_method.rb +15 -0
- data/lib/sigterm_extensions/core_ext/name_error.rb +36 -0
- data/lib/sigterm_extensions/core_ext/object.rb +3 -0
- data/lib/sigterm_extensions/core_ext/object/blank.rb +153 -0
- data/lib/sigterm_extensions/core_ext/object/colors.rb +39 -0
- data/lib/sigterm_extensions/core_ext/object/duplicable.rb +47 -0
- data/lib/sigterm_extensions/core_ext/object/inclusion.rb +27 -0
- data/lib/sigterm_extensions/core_ext/object/instance_variables.rb +28 -0
- data/lib/sigterm_extensions/core_ext/object/methods.rb +61 -0
- data/lib/sigterm_extensions/core_ext/object/with_options.rb +80 -0
- data/lib/sigterm_extensions/core_ext/range.rb +3 -0
- data/lib/sigterm_extensions/core_ext/range/compare_range.rb +74 -0
- data/lib/sigterm_extensions/core_ext/range/conversions.rb +39 -0
- data/lib/sigterm_extensions/core_ext/range/overlaps.rb +8 -0
- data/lib/sigterm_extensions/core_ext/securerandom.rb +43 -0
- data/lib/sigterm_extensions/core_ext/string.rb +3 -0
- data/lib/sigterm_extensions/core_ext/string/access.rb +93 -0
- data/lib/sigterm_extensions/core_ext/string/filters.rb +143 -0
- data/lib/sigterm_extensions/core_ext/string/starts_ends_with.rb +4 -0
- data/lib/sigterm_extensions/core_ext/string/strip.rb +25 -0
- data/lib/sigterm_extensions/core_ext/tryable.rb +132 -0
- data/lib/sigterm_extensions/descendants_tracker.rb +108 -0
- data/lib/sigterm_extensions/gem_methods.rb +47 -0
- data/lib/sigterm_extensions/hash_binding.rb +16 -0
- data/lib/sigterm_extensions/inflector.rb +339 -0
- data/lib/sigterm_extensions/inflector/acronyms.rb +42 -0
- data/lib/sigterm_extensions/inflector/inflections.rb +249 -0
- data/lib/sigterm_extensions/inflector/inflections/defaults.rb +117 -0
- data/lib/sigterm_extensions/inflector/rules.rb +37 -0
- data/lib/sigterm_extensions/inflector/version.rb +8 -0
- data/lib/sigterm_extensions/interactive_editor.rb +120 -0
- data/lib/sigterm_extensions/lazy.rb +34 -0
- data/lib/sigterm_extensions/lazy_load_hooks.rb +79 -0
- data/lib/sigterm_extensions/option_merger.rb +32 -0
- data/lib/sigterm_extensions/ordered_hash.rb +48 -0
- data/lib/sigterm_extensions/ordered_options.rb +83 -0
- data/lib/sigterm_extensions/paths.rb +235 -0
- data/lib/sigterm_extensions/per_thread_registry.rb +58 -0
- data/lib/sigterm_extensions/proxy_object.rb +14 -0
- data/lib/sigterm_extensions/staging/boot.rb +31 -0
- data/lib/sigterm_extensions/staging/boot/bundler_patch.rb +24 -0
- data/lib/sigterm_extensions/staging/boot/command.rb +26 -0
- data/lib/sigterm_extensions/staging/boot/gemfile_next_auto_sync.rb +79 -0
- data/lib/sigterm_extensions/version.rb +4 -0
- data/lib/sigterm_extensions/wrappable.rb +16 -0
- data/sigterm_extensions.gemspec +42 -0
- data/templates/dotpryrc.rb.erb +124 -0
- metadata +315 -0
|
@@ -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,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,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
|