epitools 0.5.1 → 0.5.2

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.
@@ -1,1135 +0,0 @@
1
- require 'epitools'
2
-
3
- ## Alias "Enumerator" to "Enum"
4
-
5
- if RUBY_VERSION["1.8"]
6
- require 'enumerator'
7
- Enumerator = Enumerable::Enumerator unless defined? Enumerator
8
- end
9
-
10
- unless defined? Enum
11
- if defined? Enumerator
12
- Enum = Enumerator
13
- else
14
- $stderr.puts "WARNING: Couldn't find the Enumerator class. Enum will not be available."
15
- end
16
- end
17
-
18
- RbConfig = Config unless defined? RbConfig
19
-
20
- class Object
21
-
22
- #
23
- # Slightly gross hack to add a class method.
24
- #
25
- def self.alias_class_method(dest, src)
26
- metaclass.send(:alias_method, dest, src)
27
- end
28
-
29
- #
30
- # Default "integer?" behaviour.
31
- #
32
- def integer?; false; end
33
-
34
- #
35
- # `truthy?` means `not blank?`
36
- #
37
- def truthy?
38
- if respond_to? :blank?
39
- not blank?
40
- else
41
- not nil?
42
- end
43
- end
44
-
45
- def marshal
46
- Marshal.dump self
47
- end
48
-
49
- #
50
- # Lets you say: `object.is_an? Array`
51
- #
52
- alias_method :is_an?, :is_a?
53
-
54
- end
55
-
56
- class TrueClass
57
- def truthy?; true; end
58
- end
59
-
60
- class FalseClass
61
- def truthy?; false; end
62
- end
63
-
64
- class Float
65
- #
66
- # 'true' if the float is 0.0
67
- #
68
- def blank?; self == 0.0; end
69
- end
70
-
71
- class NilClass
72
- #
73
- # Always 'true'; nil is considered blank.
74
- #
75
- def blank?; true; end
76
- end
77
-
78
- class Symbol
79
- #
80
- # Symbols are never blank.
81
- #
82
- def blank?; false; end
83
- end
84
-
85
-
86
-
87
- class Numeric
88
-
89
- def integer?; true; end
90
-
91
- def truthy?; self > 0; end
92
-
93
- def commatize
94
- to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
95
- end
96
-
97
- #
98
- # Time methods
99
- #
100
- {
101
-
102
- 'second' => 1,
103
- 'minute' => 60,
104
- 'hour' => 60 * 60,
105
- 'day' => 60 * 60 * 24,
106
- 'week' => 60 * 60 * 24 * 7,
107
- 'month' => 60 * 60 * 24 * 30,
108
- 'year' => 60 * 60 * 24 * 364.25,
109
-
110
- }.each do |unit, scale|
111
- define_method(unit) { self * scale }
112
- define_method(unit+'s') { self * scale }
113
- end
114
-
115
- def ago
116
- Time.now - self
117
- end
118
-
119
- def from_now
120
- Time.now + self
121
- end
122
-
123
- end
124
-
125
- class Integer
126
-
127
- #
128
- # 'true' if the integer is 0
129
- #
130
- def blank?; self == 0; end
131
-
132
- #
133
- # Convert the number into a hexadecimal string representation.
134
- # (Identical to to_s(16), except that numbers < 16 will have a 0 in front of them.)
135
- #
136
- def to_hex
137
- "%0.2x" % self
138
- end
139
-
140
- #
141
- # Convert the number to an array of bits (least significant digit first, or little-endian).
142
- #
143
- def to_bits
144
- # TODO: Why does thos go into an infinite loop in 1.8.7?
145
- ("%b" % self).chars.to_a.reverse.map(&:to_i)
146
- end
147
- alias_method :bits, :to_bits
148
-
149
- #
150
- # Cached constants for base62 encoding
151
- #
152
- BASE62_DIGITS = ['0'..'9', 'A'..'Z', 'a'..'z'].map(&:to_a).flatten
153
- BASE62_BASE = BASE62_DIGITS.size
154
-
155
- #
156
- # Convert a number to a string representation (in "base62" encoding).
157
- #
158
- # Base62 encoding represents the number using the characters: 0..9, A..Z, a..z
159
- #
160
- # It's the same scheme that url shorteners and YouTube uses for their
161
- # ID strings. (eg: http://www.youtube.com/watch?v=dQw4w9WgXcQ)
162
- #
163
- def to_base62
164
- result = []
165
- remainder = self
166
- max_power = ( Math.log(self) / Math.log(BASE62_BASE) ).floor
167
-
168
- max_power.downto(0) do |power|
169
- divisor = BASE62_BASE**power
170
- #p [:div, divisor, :rem, remainder]
171
- digit, remainder = remainder.divmod(divisor)
172
- result << digit
173
- end
174
-
175
- result << remainder if remainder > 0
176
-
177
- result.map{|digit| BASE62_DIGITS[digit]}.join ''
178
- end
179
-
180
- #
181
- # Returns the all the prime factors of a number.
182
- #
183
- def factors
184
- Prime # autoload the prime module
185
- prime_division.map { |n,count| [n]*count }.flatten
186
- end
187
-
188
- end
189
-
190
-
191
- #
192
- # Monkeypatch [] into Bignum and Fixnum using class_eval.
193
- #
194
- # (This is necessary because [] is defined directly on the classes, and a mixin
195
- # module will still be overridden by Big/Fixnum's native [] method.)
196
- #
197
- [Bignum, Fixnum].each do |klass|
198
-
199
- klass.class_eval do
200
-
201
- alias_method :bit, :"[]"
202
-
203
- #
204
- # Extends [] so that Integers can be sliced as if they were arrays.
205
- #
206
- def [](arg)
207
- case arg
208
- when Integer
209
- self.bit(arg)
210
- when Range
211
- self.bits[arg]
212
- end
213
- end
214
-
215
- end
216
-
217
- end
218
-
219
-
220
- class String
221
-
222
- #
223
- # Could this string be cast to an integer?
224
- #
225
- def integer?
226
- strip.match(/^\d+$/) ? true : false
227
- end
228
-
229
- #
230
- # 'true' if the string's length is 0 (after whitespace has been stripped from the ends)
231
- #
232
- def blank?
233
- strip.size == 0
234
- end
235
-
236
- #
237
- # Does this string contain something that means roughly "true"?
238
- #
239
- def truthy?
240
- case strip.downcase
241
- when "1", "true", "yes", "on", "enabled", "affirmative"
242
- true
243
- else
244
- false
245
- end
246
- end
247
-
248
- #
249
- # Convert \r\n to \n
250
- #
251
- def to_unix
252
- gsub("\r\n", "\n")
253
- end
254
-
255
- #
256
- # Remove redundant whitespaces (not including newlines).
257
- #
258
- def tighten
259
- gsub(/[\t ]+/,' ').strip
260
- end
261
-
262
- #
263
- # Remove redundant whitespace AND newlines.
264
- #
265
- def dewhitespace
266
- gsub(/\s+/,' ').strip
267
- end
268
-
269
- #
270
- # Remove ANSI color codes.
271
- #
272
- def strip_color
273
- gsub(/\e\[.*?(\d)+m/, '')
274
- end
275
- alias_method :strip_ansi, :strip_color
276
-
277
- #
278
- # Like #lines, but skips empty lines and removes \n's.
279
- #
280
- def nice_lines
281
- # note: $/ is the platform's newline separator
282
- split($/).select{|l| not l.blank? }
283
- end
284
-
285
- alias_method :nicelines, :nice_lines
286
- alias_method :clean_lines, :nice_lines
287
-
288
- #
289
- # The Infamous Caesar-Cipher. Unbreakable to this day.
290
- #
291
- def rot13
292
- tr('n-za-mN-ZA-M', 'a-zA-Z')
293
- end
294
-
295
- #
296
- # Convert non-URI characters into %XXes.
297
- #
298
- def urlencode
299
- URI.escape(self)
300
- end
301
-
302
- #
303
- # Convert an URI's %XXes into regular characters.
304
- #
305
- def urldecode
306
- URI.unescape(self)
307
- end
308
-
309
- #
310
- # Convert a query string to a hash of params
311
- #
312
- def to_params
313
- CGI.parse(self).map_values do |v|
314
- # CGI.parse wraps every value in an array. Unwrap them!
315
- if v.is_a?(Array) and v.size == 1
316
- v.first
317
- else
318
- v
319
- end
320
- end
321
- end
322
-
323
-
324
- #
325
- # Cached constants for base62 decoding.
326
- #
327
- BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.zip((0...Integer::BASE62_DIGITS.size).to_a) ]
328
- BASE62_BASE = Integer::BASE62_BASE
329
-
330
- #
331
- # Convert a string (encoded in base16 "hex" -- for example, an MD5 or SHA1 hash)
332
- # into "base62" format. (See Integer#to_base62 for more info.)
333
- #
334
- def to_base62
335
- to_i(16).to_base62
336
- end
337
-
338
- #
339
- # Convert a string encoded in base62 into an integer.
340
- # (See Integer#to_base62 for more info.)
341
- #
342
- def from_base62
343
- accumulator = 0
344
- digits = chars.map { |c| BASE62_DIGITS[c] }.reverse
345
- digits.each_with_index do |digit, power|
346
- accumulator += (BASE62_BASE**power) * digit if digit > 0
347
- end
348
- accumulator
349
- end
350
-
351
- #
352
- # Decode a mime64/base64 encoded string
353
- #
354
- def from_base64
355
- Base64.decode64 self
356
- end
357
- alias_method :decode64, :from_base64
358
-
359
- #
360
- # Encode into a mime64/base64 string
361
- #
362
- def to_base64
363
- Base64.encode64 self
364
- end
365
- alias_method :base64, :to_base64
366
- alias_method :encode64, :to_base64
367
-
368
- #
369
- # MD5 the string
370
- #
371
- def md5
372
- Digest::MD5.hexdigest self
373
- end
374
-
375
- #
376
- # SHA1 the string
377
- #
378
- def sha1
379
- Digest::SHA1.hexdigest self
380
- end
381
-
382
- #
383
- # gzip the string
384
- #
385
- def gzip(level=nil)
386
- zipped = StringIO.new
387
- Zlib::GzipWriter.wrap(zipped, level) { |io| io.write(self) }
388
- zipped.string
389
- end
390
-
391
- #
392
- # gunzip the string
393
- #
394
- def gunzip
395
- data = StringIO.new(self)
396
- Zlib::GzipReader.new(data).read
397
- end
398
-
399
- #
400
- # deflate the string
401
- #
402
- def deflate(level=nil)
403
- Zlib::Deflate.deflate(self, level)
404
- end
405
-
406
- #
407
- # inflate the string
408
- #
409
- def inflate
410
- Zlib::Inflate.inflate(self)
411
- end
412
-
413
- # `true` if this string starts with the substring
414
- #
415
- def startswith(substring)
416
- self[0...substring.size] == substring
417
- end
418
-
419
- #
420
- # `true` if this string ends with the substring
421
- #
422
- def endswith(substring)
423
- self[-substring.size..-1] == substring
424
- end
425
-
426
- #
427
- # Parse object as JSON
428
- #
429
- def from_json
430
- JSON.parse self
431
- end
432
-
433
- #
434
- # Convert the string to a Path object.
435
- #
436
- def as_path
437
- Path[self]
438
- end
439
- alias_method :to_p, :as_path
440
-
441
- def unmarshal
442
- Marshal.restore self
443
- end
444
-
445
- end
446
-
447
-
448
- class Array
449
-
450
- #
451
- # flatten.compact.uniq
452
- #
453
- def squash
454
- flatten.compact.uniq
455
- end
456
-
457
- #
458
- # Removes the elements from the array for which the block evaluates to true.
459
- # In addition, return the removed elements.
460
- #
461
- # For example, if you wanted to split an array into evens and odds:
462
- #
463
- # nums = [1,2,3,4,5,6,7,8,9,10,11,12]
464
- # even = nums.remove_if { |n| n.even? } # remove all even numbers from the "nums" array and return them
465
- # odd = nums # "nums" now only contains odd numbers
466
- #
467
- def remove_if(&block)
468
- removed = []
469
-
470
- delete_if do |x|
471
- if block.call(x)
472
- removed << x
473
- true
474
- else
475
- false
476
- end
477
- end
478
-
479
- removed
480
- end
481
-
482
- #
483
- # zip from the right (or reversed zip.)
484
- #
485
- # eg:
486
- # >> [5,39].rzip([:hours, :mins, :secs])
487
- # => [ [:mins, 5], [:secs, 39] ]
488
- #
489
- def rzip(other)
490
- # That's a lotta reverses!
491
- reverse.zip(other.reverse).reverse
492
- end
493
-
494
- #
495
- # Pick the middle element.
496
- #
497
- def middle
498
- self[(size-1) / 2]
499
- end
500
-
501
- #
502
- # XOR operator
503
- #
504
- def ^(other)
505
- (self | other) - (self & other)
506
- end
507
-
508
- #
509
- # Pick a random element.
510
- #
511
- def pick
512
- self[rand(size)]
513
- end
514
-
515
- #
516
- # Divide the array into n pieces.
517
- #
518
- def / pieces
519
- piece_size = (size.to_f / pieces).ceil
520
- each_slice(piece_size).to_a
521
- end
522
-
523
-
524
- alias_method :unzip, :transpose
525
-
526
- end
527
-
528
-
529
- module Enumerable
530
-
531
- #
532
- # 'true' if the Enumerable has no elements
533
- #
534
- def blank?
535
- not any?
536
- end
537
-
538
- #
539
- # I enjoy typing ".all" more than ".to_a"
540
- #
541
- alias_method :all, :to_a
542
-
543
- #
544
- # Split this enumerable into chunks, given some boundary condition. (Returns an array of arrays.)
545
- #
546
- # Options:
547
- # :include_boundary => true #=> include the element that you're splitting at in the results
548
- # (default: false)
549
- # :after => true #=> split after the matched element (only has an effect when used with :include_boundary)
550
- # (default: false)
551
- # :once => flase #=> only perform one split (default: false)
552
- #
553
- # Examples:
554
- # [1,2,3,4,5].split{ |e| e == 3 }
555
- # #=> [ [1,2], [4,5] ]
556
- #
557
- # [1,2,3,4,5].split(:include_boundary=>true) { |e| e == 3 }
558
- # #=> [ [1,2], [3,4,5] ]
559
- #
560
- # chapters = File.read("ebook.txt").split(/Chapter \d+/, :include_boundary=>true)
561
- # #=> [ ["Chapter 1", ...], ["Chapter 2", ...], etc. ]
562
- #
563
- def split_at(matcher=nil, options={}, &block)
564
- # TODO: Ruby 1.9 returns Enumerators for everything now. Maybe use that?
565
-
566
- return self unless self.any?
567
-
568
- include_boundary = options[:include_boundary] || false
569
-
570
- if matcher.nil?
571
- boundary_test_proc = block
572
- else
573
- if matcher.is_a? String or matcher.is_a? Regexp
574
- boundary_test_proc = proc { |element| element[matcher] rescue nil }
575
- else
576
- boundary_test_proc = proc { |element| element == matcher }
577
- #raise "I don't know how to split with #{matcher}"
578
- end
579
- end
580
-
581
- chunks = []
582
- current_chunk = []
583
-
584
- splits = 0
585
- max_splits = options[:once] == true ? 1 : options[:max_splits]
586
-
587
- each do |e|
588
-
589
- if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)
590
-
591
- if current_chunk.empty? and not include_boundary
592
- next # hit 2 boundaries in a row... just keep moving, people!
593
- end
594
-
595
- if options[:after]
596
- # split after boundary
597
- current_chunk << e if include_boundary # include the boundary, if necessary
598
- chunks << current_chunk # shift everything after the boundary into the resultset
599
- current_chunk = [] # start a new result
600
- else
601
- # split before boundary
602
- chunks << current_chunk # shift before the boundary into the resultset
603
- current_chunk = [] # start a new result
604
- current_chunk << e if include_boundary # include the boundary, if necessary
605
- end
606
-
607
- splits += 1
608
-
609
- else
610
- current_chunk << e
611
- end
612
-
613
- end
614
-
615
- chunks << current_chunk if current_chunk.any?
616
-
617
- chunks # resultset
618
- end
619
-
620
- #
621
- # Split the array into chunks, cutting between the matched element and the next element.
622
- #
623
- # Example:
624
- # [1,2,3,4].split_after{|e| e == 3 } #=> [ [1,2,3], [4] ]
625
- #
626
- def split_after(matcher=nil, options={}, &block)
627
- options[:after] ||= true
628
- options[:include_boundary] ||= true
629
- split_at(matcher, options, &block)
630
- end
631
-
632
- #
633
- # Split the array into chunks, cutting between the matched element and the previous element.
634
- #
635
- # Example:
636
- # [1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]
637
- #
638
- def split_before(matcher=nil, options={}, &block)
639
- options[:include_boundary] ||= true
640
- split_at(matcher, options, &block)
641
- end
642
-
643
- #
644
- # Sum the elements
645
- #
646
- def sum
647
- if block_given?
648
- inject(0) { |total,elem| total + yield(elem) }
649
- else
650
- inject(0) { |total,elem| total + elem }
651
- end
652
- end
653
-
654
- #
655
- # Average the elements
656
- #
657
- def average
658
- count = 0
659
- sum = inject(0) { |total,n| count += 1; total + n }
660
- sum / count.to_f
661
- end
662
-
663
- #
664
- # The same as "map", except that if an element is an Array or Enumerable, map is called
665
- # recursively on that element.
666
- #
667
- # Example:
668
- # [ [1,2], [3,4] ].map_recursively{|e| e ** 2 } #=> [ [1,4], [9,16] ]
669
- #
670
- def recursive_map(*args, &block)
671
- map(*args) do |e|
672
- if e.is_a? Array or e.is_a? Enumerable
673
- e.map(*args, &block)
674
- else
675
- block.call(e)
676
- end
677
- end
678
- end
679
-
680
- alias_method :map_recursively, :recursive_map
681
- alias_method :map_recursive, :recursive_map
682
-
683
-
684
- #
685
- # Identical to "reduce" in ruby1.9 (or foldl in haskell.)
686
- #
687
- # Example:
688
- # array.foldl{|a,b| a + b } == array[1..-1].inject(array[0]){|a,b| a + b }
689
- #
690
- def foldl(methodname=nil, &block)
691
- result = nil
692
-
693
- raise "Error: pass a parameter OR a block, not both!" unless !!methodname ^ block_given?
694
-
695
- if methodname
696
-
697
- each_with_index do |e,i|
698
- if i == 0
699
- result = e
700
- next
701
- end
702
-
703
- result = result.send(methodname, e)
704
- end
705
-
706
- else
707
-
708
- each_with_index do |e,i|
709
- if i == 0
710
- result = e
711
- next
712
- end
713
-
714
- result = block.call(result, e)
715
- end
716
-
717
- end
718
-
719
- result
720
- end
721
-
722
- #
723
- # Returns the powerset of the Enumerable
724
- #
725
- # Example:
726
- # [1,2].powerset #=> [[], [1], [2], [1, 2]]
727
- #
728
- def powerset
729
- # the bit pattern of the numbers from 0..2^(elements)-1 can be used to select the elements of the set...
730
- a = to_a
731
- (0...2**a.size).map do |bitmask|
732
- a.select.with_index{ |e, i| bitmask[i] == 1 }
733
- end
734
- end
735
-
736
- #
737
- # Does the opposite of #zip -- converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]
738
- #
739
- def unzip
740
- # TODO: make it work for arrays containing uneven-length contents
741
- to_a.transpose
742
- end
743
-
744
- #
745
- # Associative grouping; groups all elements who share something in common with each other.
746
- # You supply a block which takes two elements, and have it return true if they are "neighbours"
747
- # (eg: belong in the same group).
748
- #
749
- # Example:
750
- # [1,2,5,6].group_neighbours_by { |a,b| b-a <= 1 } #=> [ [1,2], [5,6] ]
751
- #
752
- # (Note: This is a very fast one-pass algorithm -- therefore, the groups must be pre-sorted.)
753
- #
754
- def group_neighbours_by(&block)
755
- result = []
756
- cluster = [first]
757
- each_cons(2) do |a,b|
758
- if yield(a,b)
759
- cluster << b
760
- else
761
- result << cluster
762
- cluster = [b]
763
- end
764
- end
765
-
766
- result << cluster if cluster.any?
767
-
768
- result
769
- end
770
- alias_method :group_neighbors_by, :group_neighbours_by
771
-
772
- end
773
-
774
-
775
- class Object
776
-
777
- #
778
- # Gives you a copy of the object with its attributes changed to whatever was
779
- # passed in the options hash.
780
- #
781
- # Example:
782
- # >> cookie = Cookie.new(:size=>10, :chips=>200)
783
- # => #<Cookie:0xffffffe @size=10, @chips=200>
784
- # >> cookie.with(:chips=>50)
785
- # => #<Cookie:0xfffffff @size=10, @chips=50>
786
- #
787
- # (All this method does is dup the object, then call "key=(value)" for each
788
- # key/value in the options hash.)
789
- #
790
- def with(options={})
791
- obj = dup
792
- options.each { |key, value| obj.send "#{key}=", value }
793
- obj
794
- end
795
-
796
-
797
- #
798
- # Return a copy of the class with modules mixed into it.
799
- #
800
- def self.using(*args)
801
- if block_given?
802
- yield using(*args)
803
- else
804
- copy = self.dup
805
- args.each { |arg| copy.send(:include, arg) }
806
- copy
807
- end
808
- end
809
-
810
-
811
- #
812
- # Instead of:
813
- # if cookie_jar.include? cookie
814
- # Now you can do:
815
- # if cookie.in? cookie_jar
816
- #
817
- def in?(enumerable)
818
- enumerable.include? self
819
- end
820
-
821
- #
822
- # Instead of:
823
- # @person ? @person.name : nil
824
- # Now you can do:
825
- # @person.try(:name)
826
- #
827
- def try(method, *args, &block)
828
- send(method, *args, &block) if respond_to? method
829
- end
830
-
831
- #
832
- # Benchmark a block!
833
- #
834
- def bench(message=nil)
835
- start = Time.now
836
- result = yield
837
- elapsed = Time.now - start
838
-
839
- print "[#{message}] " if message
840
- puts "elapsed time: %0.5fs" % elapsed
841
- result
842
- end
843
- alias time bench
844
-
845
-
846
- #
847
- # A decorator that makes any block-accepting method return an
848
- # Enumerator whenever the method is called without a block.
849
- #
850
- def self.enumerable *meths
851
- meths.each do |meth|
852
- alias_method "#{meth}_without_enumerator", meth
853
- class_eval %{
854
- def #{meth}(*args, &block)
855
- return Enum.new(self, #{meth.inspect}, *args, &block) unless block_given?
856
- #{meth}_without_enumerator(*args, &block)
857
- end
858
- }
859
- end
860
- end
861
-
862
- end
863
-
864
-
865
-
866
- class Hash
867
-
868
- #
869
- # 'true' if the Hash has no entries
870
- #
871
- def blank?
872
- not any?
873
- end
874
-
875
- #
876
- # Runs "remove_blank_values" on self.
877
- #
878
- def remove_blank_values!
879
- delete_if{|k,v| v.blank?}
880
- self
881
- end
882
-
883
- #
884
- # Returns a new Hash where blank values have been removed.
885
- # (It checks if the value is blank by calling #blank? on it)
886
- #
887
- def remove_blank_values
888
- dup.remove_blank_values!
889
- end
890
-
891
- #
892
- # Runs map_values on self.
893
- #
894
- def map_values!(&block)
895
- keys.each do |key|
896
- value = self[key]
897
- self[key] = yield(value)
898
- end
899
- self
900
- end
901
-
902
- #
903
- # Transforms the values of the hash by passing them into the supplied
904
- # block, and then using the block's result as the new value.
905
- #
906
- def map_values(&block)
907
- dup.map_values!(&block)
908
- end
909
-
910
- #
911
- # Runs map_keys on self.
912
- #
913
- def map_keys!(&block)
914
- keys.each do |key|
915
- value = delete(key)
916
- self[yield(key)] = value
917
- end
918
- self
919
- end
920
-
921
- #
922
- # Transforms the keys of the hash by passing them into the supplied block,
923
- # and then using the blocks result as the new key.
924
- #
925
- def map_keys(&block)
926
- dup.map_keys!(&block)
927
- end
928
-
929
- #
930
- # Returns a new Hash whose values default to empty arrays. (Good for collecting things!)
931
- #
932
- # eg:
933
- # Hash.of_arrays[:yays] << "YAY!"
934
- #
935
- def self.of_arrays
936
- new {|h,k| h[k] = [] }
937
- end
938
-
939
- #
940
- # Returns a new Hash whose values default to 0. (Good for counting things!)
941
- #
942
- # eg:
943
- # Hash.of_integers[:yays] += 1
944
- #
945
- def self.of_integers
946
- new(0)
947
- end
948
-
949
- #
950
- # Makes each element in the `path` array point to a hash containing the next element in the `path`.
951
- # Useful for turning a bunch of strings (paths, module names, etc.) into a tree.
952
- #
953
- # Example:
954
- # h = {}
955
- # h.mkdir_p(["a", "b", "c"]) #=> {"a"=>{"b"=>{"c"=>{}}}}
956
- # h.mkdir_p(["a", "b", "whoa"]) #=> {"a"=>{"b"=>{"c"=>{}, "whoa"=>{}}}}
957
- #
958
- def mkdir_p(path)
959
- return if path.empty?
960
- dir = path.first
961
- self[dir] ||= {}
962
- self[dir].mkdir_p(path[1..-1])
963
- self
964
- end
965
-
966
- #
967
- # Turn some nested hashes into a tree (returns an array of strings, padded on the left with indents.)
968
- #
969
- def tree(level=0, indent=" ")
970
- result = []
971
- dent = indent * level
972
- each do |key, val|
973
- result << dent+key
974
- result += val.tree(level+1) if val.any?
975
- end
976
- result
977
- end
978
-
979
- #
980
- # Print the result of `tree`
981
- #
982
- def print_tree
983
- tree.each { |row| puts row }
984
- nil
985
- end
986
-
987
- #
988
- # Convert the hash into a GET query.
989
- #
990
- def to_query
991
- params = ''
992
- stack = []
993
-
994
- each do |k, v|
995
- if v.is_a?(Hash)
996
- stack << [k,v]
997
- else
998
- params << "#{k}=#{v}&"
999
- end
1000
- end
1001
-
1002
- stack.each do |parent, hash|
1003
- hash.each do |k, v|
1004
- if v.is_a?(Hash)
1005
- stack << ["#{parent}[#{k}]", v]
1006
- else
1007
- params << "#{parent}[#{k}]=#{v}&"
1008
- end
1009
- end
1010
- end
1011
-
1012
- params.chop! # trailing &
1013
- params
1014
- end
1015
-
1016
- end
1017
-
1018
- unless defined?(BasicObject)
1019
- #
1020
- # A BasicObject class for Ruby 1.8
1021
- #
1022
- class BasicObject
1023
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
1024
- end
1025
- end
1026
-
1027
-
1028
-
1029
- class NotWrapper < BasicObject # :nodoc:
1030
- def initialize(orig)
1031
- @orig = orig
1032
- end
1033
-
1034
- def inspect
1035
- "{NOT #{@orig.inspect}}"
1036
- end
1037
-
1038
- def method_missing(meth, *args, &block)
1039
- result = @orig.send(meth, *args, &block)
1040
- if result.is_a? ::TrueClass or result.is_a? ::FalseClass
1041
- !result
1042
- else
1043
- raise "Sorry, I don't know how to invert #{result.inspect}"
1044
- end
1045
- end
1046
- end
1047
-
1048
- class Object
1049
-
1050
- #
1051
- # Negates a boolean, chained-method style.
1052
- #
1053
- # Example:
1054
- # >> 10.even?
1055
- # => true
1056
- # >> 10.not.even?
1057
- # => false
1058
- #
1059
- def not
1060
- NotWrapper.new(self)
1061
- end
1062
-
1063
- end
1064
-
1065
- # Metaclass
1066
- class Object
1067
- # The hidden singleton lurks behind everyone
1068
- def metaclass
1069
- class << self
1070
- self
1071
- end
1072
- end
1073
-
1074
- def meta_eval &blk
1075
- metaclass.instance_eval &blk
1076
- end
1077
-
1078
- # Adds methods to a metaclass
1079
- def meta_def name, &blk
1080
- meta_eval { define_method name, &blk }
1081
- end
1082
-
1083
- # Defines an instance method within a class
1084
- def class_def name, &blk
1085
- class_eval { define_method name, &blk }
1086
- end
1087
- end
1088
-
1089
- unless IO.respond_to? :copy_stream
1090
-
1091
- class IO
1092
-
1093
- def self.copy_stream(input, output)
1094
- while chunk = input.read(8192)
1095
- output.write(chunk)
1096
- end
1097
- end
1098
-
1099
- end
1100
-
1101
- end
1102
-
1103
- #
1104
- # Emit a quick debug message (only if $DEBUG is true)
1105
- #
1106
- def dmsg(msg)
1107
- if $DEBUG
1108
- case msg
1109
- when String
1110
- puts msg
1111
- else
1112
- puts msg.inspect
1113
- end
1114
- end
1115
- end
1116
-
1117
-
1118
- def del(x)
1119
- case thing
1120
- when String
1121
- del(x.to_sym)
1122
- when Class, Module
1123
- Object.send(:remove_const, x)
1124
- when Method
1125
- x.owner.send(:undef_method, x.name)
1126
- when Symbol
1127
- if Object.const_get(x)
1128
- Object.send(:remove_const, x)
1129
- elsif method(x)
1130
- undef_method x
1131
- end
1132
- else
1133
- raise "Error: don't know how to 'del #{x.inspect}'"
1134
- end
1135
- end