senv 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README +14 -0
- data/Rakefile +576 -0
- data/a.rb +92 -0
- data/bin/senv +259 -0
- data/dist/senv-0.4.2-linux-x86.tgz +0 -0
- data/dist/senv-0.4.2-linux-x86_64.tgz +0 -0
- data/dist/senv-0.4.2-osx.tgz +0 -0
- data/dist/senv.rb +1468 -0
- data/dist/senv.sh +11 -0
- data/lib/senv.rb +663 -0
- data/lib/senv/script.rb +527 -0
- data/senv.gemspec +44 -0
- metadata +56 -0
data/dist/senv.sh
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
# Figure out where this script is located.
|
5
|
+
SELFDIR="`dirname \"$0\"`"
|
6
|
+
SELFDIR="`cd \"$SELFDIR\" && pwd`"
|
7
|
+
|
8
|
+
# Run the actual app using the bundled Ruby interpreter.
|
9
|
+
exec "$SELFDIR/lib/ruby/bin/ruby" "$SELFDIR/lib/app/senv.rb" "$@"
|
10
|
+
|
11
|
+
# Thanks @FooBarWidget! ref: https://github.com/phusion/traveling-ruby
|
data/lib/senv.rb
ADDED
@@ -0,0 +1,663 @@
|
|
1
|
+
#
|
2
|
+
require 'erb'
|
3
|
+
require 'yaml'
|
4
|
+
require 'json'
|
5
|
+
require 'rbconfig'
|
6
|
+
require 'pp'
|
7
|
+
require 'time'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'pathname'
|
10
|
+
require 'thread'
|
11
|
+
require 'openssl'
|
12
|
+
require 'tmpdir'
|
13
|
+
require 'securerandom'
|
14
|
+
|
15
|
+
#
|
16
|
+
module Senv
|
17
|
+
#
|
18
|
+
VERSION = '0.4.2'.freeze
|
19
|
+
|
20
|
+
def Senv.version
|
21
|
+
VERSION
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
LICENSE = 'MIT'.freeze
|
26
|
+
|
27
|
+
def Senv.license
|
28
|
+
LICENSE
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
SUMMARY = ''
|
33
|
+
|
34
|
+
#
|
35
|
+
DEFAULT = 'development'.freeze
|
36
|
+
|
37
|
+
def Senv.default
|
38
|
+
DEFAULT
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
class Error < StandardError
|
43
|
+
end
|
44
|
+
|
45
|
+
def Senv.error!(*args, &block)
|
46
|
+
raise Error.new(*args, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
def Senv.env
|
51
|
+
ENV['SENV']
|
52
|
+
end
|
53
|
+
|
54
|
+
def Senv.env=(env)
|
55
|
+
if env
|
56
|
+
ENV['SENV'] = env.to_s.strip
|
57
|
+
else
|
58
|
+
ENV.delete('SENV')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def Senv.load(*args)
|
64
|
+
Senv.thread_safe do
|
65
|
+
#
|
66
|
+
env, options = Senv.parse_load_args(*args)
|
67
|
+
|
68
|
+
#
|
69
|
+
force = !!(options['force'] || options[:force])
|
70
|
+
|
71
|
+
#
|
72
|
+
a_parent_process_has_already_loaded_the_senv = (
|
73
|
+
ENV['SENV'] == env &&
|
74
|
+
ENV['SENV_LOADED'] &&
|
75
|
+
ENV['SENV_ENVIRONMENT']
|
76
|
+
)
|
77
|
+
|
78
|
+
if(a_parent_process_has_already_loaded_the_senv && !force)
|
79
|
+
Senv.env = env
|
80
|
+
Senv.loaded = JSON.parse(ENV['SENV_LOADED'])
|
81
|
+
Senv.environment = JSON.parse(ENV['SENV_ENVIRONMENT'])
|
82
|
+
return env
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
unless Senv.loading
|
87
|
+
loading = Senv.loading
|
88
|
+
|
89
|
+
begin
|
90
|
+
Senv.loading = env
|
91
|
+
|
92
|
+
Senv.loaded.clear
|
93
|
+
Senv.environment.clear
|
94
|
+
|
95
|
+
Senv.load_config_paths_for(env)
|
96
|
+
|
97
|
+
Senv.env = env
|
98
|
+
|
99
|
+
ENV['SENV'] = Senv.env
|
100
|
+
ENV['SENV_LOADED'] = JSON.generate(Senv.loaded)
|
101
|
+
ENV['SENV_ENVIRONMENT'] = JSON.generate(Senv.environment)
|
102
|
+
|
103
|
+
return env
|
104
|
+
ensure
|
105
|
+
Senv.loading = loading
|
106
|
+
end
|
107
|
+
else
|
108
|
+
a_config_file_imports_another_senv = (
|
109
|
+
Senv.loading != env
|
110
|
+
)
|
111
|
+
|
112
|
+
if a_config_file_imports_another_senv
|
113
|
+
Senv.load_config_paths_for(env)
|
114
|
+
return env
|
115
|
+
end
|
116
|
+
|
117
|
+
a_config_file_imports_itself_recursively = (
|
118
|
+
Senv.loading == env
|
119
|
+
)
|
120
|
+
|
121
|
+
if a_config_file_imports_itself_recursively
|
122
|
+
:cowardly_refuse_to_infinitely_recurse
|
123
|
+
return nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def Senv.load!(*args)
|
130
|
+
env, options = Senv.parse_load_args(*args)
|
131
|
+
options['force'] = options[:force] = true
|
132
|
+
Senv.load(env, options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def Senv.parse_load_args(*args)
|
136
|
+
env = Senv.env || Senv.default
|
137
|
+
options = Hash.new
|
138
|
+
|
139
|
+
case args.first
|
140
|
+
when String, Symbol
|
141
|
+
env = args.shift.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
case args.first
|
145
|
+
when Hash
|
146
|
+
options = args.shift
|
147
|
+
end
|
148
|
+
|
149
|
+
[env, options]
|
150
|
+
end
|
151
|
+
|
152
|
+
def Senv.thread_safe(&block)
|
153
|
+
THREAD_SAFE.synchronize(&block)
|
154
|
+
end
|
155
|
+
THREAD_SAFE = ::Monitor.new
|
156
|
+
|
157
|
+
def Senv.load_config_paths_for(env)
|
158
|
+
paths = Senv.config_paths_for(env)
|
159
|
+
Senv.load_config_paths(*paths)
|
160
|
+
end
|
161
|
+
|
162
|
+
def Senv.config_paths_for(env)
|
163
|
+
glob = "**/#{ env }.{rb,enc.rb}"
|
164
|
+
|
165
|
+
Senv.directory.glob(glob).sort_by do |path|
|
166
|
+
ext = path.basename.extname.split('.')
|
167
|
+
[path.basename.to_s.size, ext.size]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def Senv.load_config_paths(*paths)
|
172
|
+
libs = []
|
173
|
+
configs = []
|
174
|
+
|
175
|
+
paths.each do |path|
|
176
|
+
exts = path.extname.split('.')[1..-1]
|
177
|
+
|
178
|
+
case
|
179
|
+
when exts.include?('rb')
|
180
|
+
libs << path
|
181
|
+
else
|
182
|
+
configs << path
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
{
|
187
|
+
libs => :load_lib,
|
188
|
+
configs => :load_config,
|
189
|
+
}.each do |list, loader|
|
190
|
+
list.each do |path|
|
191
|
+
path = path.to_s
|
192
|
+
|
193
|
+
Senv.debug({'loading' => path})
|
194
|
+
|
195
|
+
if Senv.loaded.has_key?(path)
|
196
|
+
Senv.debug({'skipping' => path})
|
197
|
+
next
|
198
|
+
end
|
199
|
+
|
200
|
+
Senv.loaded[path] = nil
|
201
|
+
|
202
|
+
captured =
|
203
|
+
Senv.capturing_environment_changes do
|
204
|
+
Senv.send(loader, path)
|
205
|
+
end
|
206
|
+
|
207
|
+
changes = captured.changes
|
208
|
+
|
209
|
+
Senv.debug({path => changes})
|
210
|
+
|
211
|
+
Senv.loaded[path] = changes
|
212
|
+
|
213
|
+
captured.apply(Senv.environment)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
def Senv.loading
|
220
|
+
@loading
|
221
|
+
end
|
222
|
+
|
223
|
+
def Senv.loading=(env)
|
224
|
+
@loading = env.to_s.strip
|
225
|
+
end
|
226
|
+
|
227
|
+
def Senv.loaded
|
228
|
+
@loaded ||= {}
|
229
|
+
end
|
230
|
+
|
231
|
+
def Senv.loaded=(hash)
|
232
|
+
@loaded = hash
|
233
|
+
end
|
234
|
+
|
235
|
+
def Senv.environment
|
236
|
+
@environment ||= {}
|
237
|
+
end
|
238
|
+
|
239
|
+
def Senv.environment=(hash)
|
240
|
+
@environment = hash
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
def Senv.realpath(path)
|
245
|
+
Pathname.new(path.to_s).realpath
|
246
|
+
end
|
247
|
+
|
248
|
+
def Senv.expand_path(path)
|
249
|
+
(realpath(path) rescue File.expand_path(path)).to_s
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
def Senv.root
|
254
|
+
determine_root! unless @root
|
255
|
+
@root
|
256
|
+
end
|
257
|
+
|
258
|
+
def Senv.root=(root)
|
259
|
+
@root = realpath(root)
|
260
|
+
end
|
261
|
+
|
262
|
+
def Senv.determine_root!
|
263
|
+
if ENV['SENV_ROOT']
|
264
|
+
Senv.root = ENV['SENV_ROOT']
|
265
|
+
return @root
|
266
|
+
else
|
267
|
+
Senv.search_path.each do |dirname|
|
268
|
+
if test(?d, dirname)
|
269
|
+
Senv.root = dirname
|
270
|
+
return @root
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
msg = "[SENV] no `.senv` directory found via `#{ Senv.search_path.join(' | ') }`"
|
276
|
+
Senv.error!(msg)
|
277
|
+
end
|
278
|
+
|
279
|
+
def Senv.directory
|
280
|
+
Senv.root.join('.senv')
|
281
|
+
end
|
282
|
+
|
283
|
+
def Senv.search_path
|
284
|
+
search_path = []
|
285
|
+
|
286
|
+
if ENV['SENV_PATH']
|
287
|
+
ENV['SENV_PATH'].split(':').each do |path|
|
288
|
+
search_path << Senv.expand_path(path).to_s
|
289
|
+
end
|
290
|
+
else
|
291
|
+
Pathname.pwd.realpath.ascend do |path|
|
292
|
+
search_path << path.to_s
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
search_path
|
297
|
+
end
|
298
|
+
|
299
|
+
def Senv.key_path
|
300
|
+
Senv.directory.join('_key')
|
301
|
+
end
|
302
|
+
|
303
|
+
def Senv.key
|
304
|
+
if ENV['SENV_KEY']
|
305
|
+
ENV['SENV_KEY']
|
306
|
+
else
|
307
|
+
if Senv.key_path.exist?
|
308
|
+
Senv.key_path.binread.strip
|
309
|
+
else
|
310
|
+
msg = "Senv.key not found in : #{ Senv.key_path }"
|
311
|
+
Senv.error!(msg)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def Senv.key=(key)
|
317
|
+
ENV['SENV_KEY'] = key.to_s.strip
|
318
|
+
end
|
319
|
+
|
320
|
+
def Senv.key_source
|
321
|
+
if ENV['SENV_KEY']
|
322
|
+
"ENV['SENV_KEY']"
|
323
|
+
else
|
324
|
+
Senv.key_path rescue '(no key source)'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
#
|
329
|
+
ENCRYPTED_PATH_RE = Regexp.union(/\.enc(rypted)$/, /\.enc(rypted)?\./)
|
330
|
+
|
331
|
+
def Senv.is_encrypted?(path)
|
332
|
+
path.to_s =~ ENCRYPTED_PATH_RE
|
333
|
+
end
|
334
|
+
|
335
|
+
#
|
336
|
+
def Senv.binread(path, options = {})
|
337
|
+
data = IO.binread(path)
|
338
|
+
|
339
|
+
encrypted =
|
340
|
+
if options.has_key?('encrypted')
|
341
|
+
options['encrypted']
|
342
|
+
else
|
343
|
+
Senv.is_encrypted?(path)
|
344
|
+
end
|
345
|
+
|
346
|
+
if encrypted
|
347
|
+
data =
|
348
|
+
begin
|
349
|
+
Blowfish.decrypt(Senv.key, data)
|
350
|
+
rescue
|
351
|
+
abort "could not decrypt `#{ path }` with key `#{ Senv.key }` from `#{ Senv.key_source }`"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
data
|
356
|
+
end
|
357
|
+
|
358
|
+
def Senv.read(path, options = {})
|
359
|
+
Senv.binread(path)
|
360
|
+
end
|
361
|
+
|
362
|
+
def Senv.binwrite(path, data, options = {})
|
363
|
+
encrypted =
|
364
|
+
if options.has_key?('encrypted')
|
365
|
+
options['encrypted']
|
366
|
+
else
|
367
|
+
Senv.is_encrypted?(path)
|
368
|
+
end
|
369
|
+
|
370
|
+
if encrypted
|
371
|
+
data =
|
372
|
+
begin
|
373
|
+
Blowfish.encrypt(Senv.key, data)
|
374
|
+
rescue
|
375
|
+
abort "could not encrypt `#{ data.to_s.split("\n").first }...` with key `#{ Senv.key }` from `#{ Senv.key_source }`"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
IO.binwrite(path, data)
|
380
|
+
end
|
381
|
+
|
382
|
+
def Senv.write(path, data, options = {})
|
383
|
+
Senv.binwrite(path, data, options)
|
384
|
+
end
|
385
|
+
|
386
|
+
def Senv.load_lib(path)
|
387
|
+
#
|
388
|
+
code = Senv.binread(path.to_s)
|
389
|
+
binding = ::TOPLEVEL_BINDING
|
390
|
+
filename = path.to_s
|
391
|
+
|
392
|
+
#
|
393
|
+
Kernel.eval(code, binding, filename)
|
394
|
+
end
|
395
|
+
|
396
|
+
def Senv.load_config(path)
|
397
|
+
#
|
398
|
+
erb = Senv.binread(path)
|
399
|
+
expanded = ERB.new(erb).result(::TOPLEVEL_BINDING)
|
400
|
+
buf = expanded
|
401
|
+
|
402
|
+
#
|
403
|
+
encoded = buf
|
404
|
+
|
405
|
+
config =
|
406
|
+
case
|
407
|
+
when path =~ /yml|yaml/
|
408
|
+
YAML.load(encoded)
|
409
|
+
when path =~ /json/
|
410
|
+
JSON.parse(encoded)
|
411
|
+
else
|
412
|
+
abort "unknown config format in #{ path }"
|
413
|
+
end
|
414
|
+
|
415
|
+
#
|
416
|
+
unless config && config.is_a?(Hash)
|
417
|
+
abort "[SENV] failed to load #{ path }"
|
418
|
+
end
|
419
|
+
|
420
|
+
#
|
421
|
+
config.each do |key, val|
|
422
|
+
ENV[key.to_s] = val.to_s
|
423
|
+
end
|
424
|
+
|
425
|
+
#
|
426
|
+
config
|
427
|
+
end
|
428
|
+
|
429
|
+
#
|
430
|
+
def Senv.debug(*args, &block)
|
431
|
+
if args.empty? && block.nil?
|
432
|
+
return Senv.debug?
|
433
|
+
end
|
434
|
+
|
435
|
+
return nil unless Senv.debug?
|
436
|
+
|
437
|
+
lines = []
|
438
|
+
|
439
|
+
args.each do |arg|
|
440
|
+
case
|
441
|
+
when arg.is_a?(String)
|
442
|
+
lines << arg.strip
|
443
|
+
else
|
444
|
+
lines << arg.inspect.strip
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
return nil if(lines.empty? && block.nil?)
|
449
|
+
|
450
|
+
if lines
|
451
|
+
lines.each do |line|
|
452
|
+
STDERR.puts "# [SENV=#{ Senv.env }] : #{ line }"
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
if block
|
457
|
+
return block.call
|
458
|
+
else
|
459
|
+
true
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def Senv.debug?
|
464
|
+
!!ENV['SENV_DEBUG']
|
465
|
+
end
|
466
|
+
|
467
|
+
def Senv.debug=(arg)
|
468
|
+
if arg
|
469
|
+
ENV['SENV_DEBUG'] = 'true'
|
470
|
+
else
|
471
|
+
ENV.delete('SENV_DEBUG')
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
#
|
476
|
+
def Senv.senvs
|
477
|
+
@senvs ||= Hash.new
|
478
|
+
end
|
479
|
+
|
480
|
+
def Senv.for_senv!(senv)
|
481
|
+
senv = senv.to_s
|
482
|
+
|
483
|
+
IO.popen('-', 'w+') do |io|
|
484
|
+
child = io.nil?
|
485
|
+
|
486
|
+
if child
|
487
|
+
Senv.load(senv)
|
488
|
+
puts Senv.environment.to_yaml
|
489
|
+
exit
|
490
|
+
else
|
491
|
+
YAML.load(io.read)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
def Senv.for_senv(senv)
|
497
|
+
senv = senv.to_s
|
498
|
+
|
499
|
+
if Senv.senvs.has_key?(senv)
|
500
|
+
return Senv.senvs[senv]
|
501
|
+
end
|
502
|
+
|
503
|
+
Senv.senvs[senv] = Senv.for_senv!(senv)
|
504
|
+
end
|
505
|
+
|
506
|
+
def Senv.get(senv, var)
|
507
|
+
Senv.for_senv(senv)[var.to_s]
|
508
|
+
end
|
509
|
+
|
510
|
+
def Senv.get!(senv, var)
|
511
|
+
Senv.for_senv!(senv)[var.to_s]
|
512
|
+
end
|
513
|
+
|
514
|
+
#
|
515
|
+
module Blowfish
|
516
|
+
def cipher(senv, key, data)
|
517
|
+
cipher = OpenSSL::Cipher.new('bf-cbc').send(senv)
|
518
|
+
cipher.key = Digest::SHA256.digest(key.to_s).slice(0,16)
|
519
|
+
cipher.update(data) << cipher.final
|
520
|
+
end
|
521
|
+
|
522
|
+
def encrypt(key, data)
|
523
|
+
cipher(:encrypt, key, data)
|
524
|
+
end
|
525
|
+
|
526
|
+
def decrypt(key, text)
|
527
|
+
cipher(:decrypt, key, text)
|
528
|
+
end
|
529
|
+
|
530
|
+
def cycle(key, data)
|
531
|
+
decrypt(key, encrypt(key, data))
|
532
|
+
end
|
533
|
+
|
534
|
+
def recrypt(old_key, new_key, data)
|
535
|
+
encrypt(new_key, decrypt(old_key, data))
|
536
|
+
end
|
537
|
+
|
538
|
+
extend(self)
|
539
|
+
end
|
540
|
+
|
541
|
+
#
|
542
|
+
def Senv.capturing_environment_changes(&block)
|
543
|
+
EnvChangeTracker.track(&block)
|
544
|
+
end
|
545
|
+
|
546
|
+
class EnvChangeTracker < ::BasicObject
|
547
|
+
def initialize(env)
|
548
|
+
@env = env
|
549
|
+
|
550
|
+
@changes = {
|
551
|
+
:deleted => [],
|
552
|
+
:updated => [],
|
553
|
+
:created => [],
|
554
|
+
}
|
555
|
+
|
556
|
+
@change_for = proc do |key, val|
|
557
|
+
if @env.has_key?(key)
|
558
|
+
case
|
559
|
+
when val.nil?
|
560
|
+
{:type => :deleted, :info => [key, val]}
|
561
|
+
when val.to_s != @env[key].to_s
|
562
|
+
{:type => :updated, :info => [key, val]}
|
563
|
+
else
|
564
|
+
nil
|
565
|
+
end
|
566
|
+
else
|
567
|
+
{:type => :created, :info => [key, val]}
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
@track_change = proc do |key, val|
|
572
|
+
change = @change_for[key, val]
|
573
|
+
|
574
|
+
if change
|
575
|
+
@changes[change[:type]].push(change[:info])
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
def changes
|
581
|
+
@changes
|
582
|
+
end
|
583
|
+
|
584
|
+
def method_missing(method, *args, &block)
|
585
|
+
@env.send(method, *args, &block)
|
586
|
+
end
|
587
|
+
|
588
|
+
def []=(key, val)
|
589
|
+
@track_change[key, val]
|
590
|
+
@env[key] = val
|
591
|
+
end
|
592
|
+
|
593
|
+
def replace(hash)
|
594
|
+
hash.each do |key, val|
|
595
|
+
@track_change[key, val]
|
596
|
+
end
|
597
|
+
@env.replace(hash)
|
598
|
+
end
|
599
|
+
|
600
|
+
def store(key, val)
|
601
|
+
@track_change[key, val]
|
602
|
+
@env.store(key, val)
|
603
|
+
end
|
604
|
+
|
605
|
+
def delete(key)
|
606
|
+
@track_change[key, nil]
|
607
|
+
@env.delete(key)
|
608
|
+
end
|
609
|
+
|
610
|
+
def apply(env)
|
611
|
+
@changes[:created].each do |k, v|
|
612
|
+
env[k] = v
|
613
|
+
end
|
614
|
+
@changes[:updated].each do |k, v|
|
615
|
+
env[k] = v
|
616
|
+
end
|
617
|
+
@changes[:deleted].each do |k, v|
|
618
|
+
env.delete(k)
|
619
|
+
end
|
620
|
+
@changes
|
621
|
+
end
|
622
|
+
|
623
|
+
THREAD_SAFE = ::Monitor.new
|
624
|
+
|
625
|
+
def EnvChangeTracker.track(&block)
|
626
|
+
THREAD_SAFE.synchronize do
|
627
|
+
env = EnvChangeTracker.new(::ENV)
|
628
|
+
|
629
|
+
::Object.send(:remove_const, :ENV)
|
630
|
+
::Object.send(:const_set, :ENV, env)
|
631
|
+
|
632
|
+
begin
|
633
|
+
block.call
|
634
|
+
env
|
635
|
+
ensure
|
636
|
+
::Object.send(:remove_const, :ENV)
|
637
|
+
::Object.send(:const_set, :ENV, env)
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
#
|
644
|
+
class ::Pathname
|
645
|
+
unless ::Pathname.pwd.respond_to?(:glob)
|
646
|
+
def glob(glob, &block)
|
647
|
+
paths = []
|
648
|
+
|
649
|
+
Dir.glob("#{ self }/#{ glob }") do |entry|
|
650
|
+
path = Pathname.new(entry)
|
651
|
+
|
652
|
+
if block
|
653
|
+
block.call(path)
|
654
|
+
else
|
655
|
+
paths.push(path)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
block ? nil : paths
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|