epitools 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,254 @@
1
+ class Numeric
2
+
3
+ def integer?; true; end
4
+
5
+ def truthy?; self > 0; end
6
+
7
+ def commatize
8
+ to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
9
+ end
10
+
11
+ #
12
+ # Time methods
13
+ #
14
+ {
15
+
16
+ 'second' => 1,
17
+ 'minute' => 60,
18
+ 'hour' => 60 * 60,
19
+ 'day' => 60 * 60 * 24,
20
+ 'week' => 60 * 60 * 24 * 7,
21
+ 'month' => 60 * 60 * 24 * 30,
22
+ 'year' => 60 * 60 * 24 * 365,
23
+
24
+ }.each do |unit, scale|
25
+ define_method(unit) { self * scale }
26
+ define_method(unit+'s') { self * scale }
27
+ end
28
+
29
+ def ago
30
+ Time.now - self
31
+ end
32
+
33
+ def from_now
34
+ Time.now + self
35
+ end
36
+
37
+ #
38
+ # If `n.times` is like `each`, `n.things` is like `map`. Return
39
+ #
40
+ def things(&block)
41
+ if block_given?
42
+ Array.new(self, &block)
43
+ else
44
+ Array.new(self)
45
+ end
46
+ end
47
+
48
+ [:cos,
49
+ :sin,
50
+ :tan,
51
+ :acos,
52
+ :asin,
53
+ :atan,
54
+ :cosh,
55
+ :sinh,
56
+ :tanh,
57
+ :acosh,
58
+ :asinh,
59
+ :atanh,
60
+ :exp,
61
+ :log2,
62
+ :log10,
63
+ :sqrt,
64
+ :cbrt,
65
+ :frexp,
66
+ :erf,
67
+ :erfc,
68
+ :gamma,
69
+ :lgamma
70
+ ].each do |meth|
71
+
72
+ class_eval %{
73
+ def #{meth}
74
+ Math.#{meth}(self)
75
+ end
76
+ }
77
+
78
+ end
79
+
80
+ def log(n)
81
+ Math.log(self, n)
82
+ end
83
+
84
+ end
85
+
86
+
87
+ class Integer
88
+
89
+ #
90
+ # 'true' if the integer is 0
91
+ #
92
+ def blank?; self == 0; end
93
+
94
+ #
95
+ # Convert the number into a hexadecimal string representation.
96
+ # (Identical to to_s(16), except that numbers < 16 will have a 0 in front of them.)
97
+ #
98
+ def to_hex
99
+ "%0.2x" % self
100
+ end
101
+
102
+ #
103
+ # Convert the number to an array of bits (least significant digit first, or little-endian).
104
+ #
105
+ def to_bits
106
+ # TODO: Why does thos go into an infinite loop in 1.8.7?
107
+ ("%b" % self).chars.to_a.reverse.map(&:to_i)
108
+ end
109
+ alias_method :bits, :to_bits
110
+
111
+ #
112
+ # Cached constants for base62 encoding
113
+ #
114
+ BASE62_DIGITS = ['0'..'9', 'A'..'Z', 'a'..'z'].map(&:to_a).flatten
115
+ BASE62_BASE = BASE62_DIGITS.size
116
+
117
+ #
118
+ # Convert a number to a string representation (in "base62" encoding).
119
+ #
120
+ # Base62 encoding represents the number using the characters: 0..9, A..Z, a..z
121
+ #
122
+ # It's the same scheme that url shorteners and YouTube uses for their
123
+ # ID strings. (eg: http://www.youtube.com/watch?v=dQw4w9WgXcQ)
124
+ #
125
+ def to_base62
126
+ result = []
127
+ remainder = self
128
+ max_power = ( Math.log(self) / Math.log(BASE62_BASE) ).floor
129
+
130
+ max_power.downto(0) do |power|
131
+ divisor = BASE62_BASE**power
132
+ #p [:div, divisor, :rem, remainder]
133
+ digit, remainder = remainder.divmod(divisor)
134
+ result << digit
135
+ end
136
+
137
+ result << remainder if remainder > 0
138
+
139
+ result.map{|digit| BASE62_DIGITS[digit]}.join ''
140
+ end
141
+
142
+ #
143
+ # Returns the all the prime factors of a number.
144
+ #
145
+ def factors
146
+ Prime # autoload the prime module
147
+ prime_division.map { |n,count| [n]*count }.flatten
148
+ end
149
+
150
+ #
151
+ # Factorial (iterated style)
152
+ #
153
+ def fact
154
+ total = 1
155
+ self.downto(2) { |x| total *= x }
156
+ total
157
+ end
158
+ alias_method :factorial, :fact
159
+
160
+ #
161
+ # Fibonacci (recursive style)
162
+ #
163
+ def fib
164
+ self < 2 ? self : (self-1).fib + (self-2).fib
165
+ end
166
+ alias_method :fibonacci, :fib
167
+
168
+ #
169
+ # Convert seconds to hours:minutes:seconds (hours is dropped if it's zero)
170
+ #
171
+ def to_hms
172
+ seconds = self
173
+
174
+ days, seconds = seconds.divmod(86400)
175
+ hours, seconds = seconds.divmod(3600)
176
+ minutes, seconds = seconds.divmod(60)
177
+
178
+ result = "%0.2d:%0.2d" % [minutes,seconds]
179
+ result = ("%0.2d:" % hours) + result if hours > 0 or days > 0
180
+ result = ("%0.2d:" % days) + result if days > 0
181
+
182
+ result
183
+ end
184
+
185
+ end
186
+
187
+ #
188
+ # Monkeypatch [] into Bignum and Fixnum using class_eval.
189
+ #
190
+ # (This is necessary because [] is defined directly on the classes, and a mixin
191
+ # module will still be overridden by Big/Fixnum's native [] method.)
192
+ #
193
+ [Bignum, Fixnum].each do |klass|
194
+
195
+ klass.class_eval do
196
+
197
+ alias_method :bit, :"[]"
198
+
199
+ #
200
+ # Extends [] so that Integers can be sliced as if they were arrays.
201
+ #
202
+ def [](arg)
203
+ case arg
204
+ when Integer
205
+ self.bit(arg)
206
+ when Range
207
+ self.bits[arg]
208
+ end
209
+ end
210
+
211
+ end
212
+
213
+ end
214
+
215
+ class Time
216
+
217
+ #
218
+ # Relative time, in words. (eg: "1 second ago", "2 weeks from now", etc.)
219
+ #
220
+ def in_words
221
+ delta = (Time.now-self).to_i
222
+ a = delta.abs
223
+
224
+ amount = case a
225
+ when 0
226
+ 'just now'
227
+ when 1
228
+ '1 second'
229
+ when 2..59
230
+ "second".amount(a)
231
+ when 1.minute...1.hour
232
+ "minute".amount(a/1.minute)
233
+ when 1.hour...1.day
234
+ "hour".amount(a/1.hour)
235
+ when 1.day...7.days
236
+ "day".amount(a/1.day)
237
+ when 1.week...1.month
238
+ "week".amount(a/1.week)
239
+ when 1.month...12.months
240
+ "month".amount(a/1.month)
241
+ else
242
+ "year".amount(a/1.year)
243
+ end
244
+
245
+ if delta < 0
246
+ amount += " from now"
247
+ elsif delta > 0
248
+ amount += " ago"
249
+ end
250
+
251
+ amount
252
+ end
253
+
254
+ end
@@ -0,0 +1,195 @@
1
+ class Object
2
+
3
+ #
4
+ # Return a hash of local variables in the caller's scope: {:variable_name=>value}
5
+ #
6
+ def locals
7
+ require 'binding_of_caller'
8
+ caller = binding.of_caller(1)
9
+ vars = caller.eval("local_variables").reject{|e| e[/^_/]}
10
+ vals = caller.eval("[ #{vars.join(",")} ]")
11
+ Hash[ vars.zip(vals) ]
12
+ end
13
+
14
+
15
+ #
16
+ # Gives you a copy of the object with its attributes changed to whatever was
17
+ # passed in the options hash.
18
+ #
19
+ # Example:
20
+ # >> cookie = Cookie.new(:size=>10, :chips=>200)
21
+ # => #<Cookie:0xffffffe @size=10, @chips=200>
22
+ # >> cookie.with(:chips=>50)
23
+ # => #<Cookie:0xfffffff @size=10, @chips=50>
24
+ #
25
+ # (All this method does is dup the object, then call "key=(value)" for each
26
+ # key/value in the options hash.)
27
+ #
28
+ def with(options={})
29
+ obj = dup
30
+ options.each { |key, value| obj.send "#{key}=", value }
31
+ obj
32
+ end
33
+
34
+ #
35
+ # Return a copy of the class with modules mixed into it.
36
+ #
37
+ def self.using(*args)
38
+ if block_given?
39
+ yield using(*args)
40
+ else
41
+ copy = self.dup
42
+ args.each { |arg| copy.send(:include, arg) }
43
+ copy
44
+ end
45
+ end
46
+
47
+ #
48
+ # Turns block-accepting iterator methods (eg: each) into methods that return an
49
+ # Enumerator when they're called called without a block.
50
+ #
51
+ # It can transform many methods at once (eg: `enumerable :each, :all_people`).
52
+ #
53
+ # Example:
54
+ #
55
+ # def lots_of_stuff
56
+ # @stuff.each { |thing| yield thing }
57
+ # end
58
+ #
59
+ # enumerable :lots_of_stuff
60
+ #
61
+ # Now you can use it like an Enumerator: object.lots_of_stuff.with_index.sort.zip(99..1000)
62
+ #
63
+ def self.enumerable *meths
64
+ meths.each do |meth|
65
+ alias_method "#{meth}_without_enumerator", meth
66
+ class_eval %{
67
+ def #{meth}(*args, &block)
68
+ return Enum.new(self, #{meth.inspect}, *args, &block) unless block_given?
69
+ #{meth}_without_enumerator(*args, &block)
70
+ end
71
+ }
72
+ end
73
+ end
74
+ alias_class_method :enumerator, :enumerable
75
+
76
+ #
77
+ # Instead of:
78
+ # if cookie_jar.include? cookie
79
+ # Now you can do:
80
+ # if cookie.in? cookie_jar
81
+ #
82
+ def in?(enum)
83
+ enum.include? self
84
+ end
85
+
86
+ #
87
+ # Instead of:
88
+ # @person ? @person.name : nil
89
+ # Now you can do:
90
+ # @person.try(:name)
91
+ #
92
+ def try(method, *args, &block)
93
+ send(method, *args, &block) if respond_to? method
94
+ end
95
+
96
+ #
97
+ # Serialize this object to a binary String, using Marshal.dump.
98
+ #
99
+ def marshal
100
+ Marshal.dump self
101
+ end
102
+ alias_method :to_marshal, :marshal
103
+
104
+ #
105
+ # Serialize this object to YAML.
106
+ #
107
+ def to_yaml
108
+ YAML::dump(self)
109
+ end
110
+
111
+ #
112
+ # Serialize this object to JSON (defaults to "pretty" indented JSON).
113
+ #
114
+ def to_json(pretty=true)
115
+ pretty ? JSON::pretty_generate(self) : JSON.dump(self)
116
+ end
117
+
118
+ #
119
+ # Proper grammar.
120
+ #
121
+ alias_method :is_an?, :is_a?
122
+ alias_method :responds_to?, :respond_to?
123
+
124
+ #
125
+ # Emit a quick debug message (only if $DEBUG is true)
126
+ #
127
+ def dmsg(msg)
128
+ if $DEBUG
129
+ case msg
130
+ when String
131
+ puts msg
132
+ else
133
+ puts msg.inspect
134
+ end
135
+ end
136
+ end
137
+
138
+ #
139
+ # Time a block.
140
+ #
141
+ def time(message=nil)
142
+ start = Time.now
143
+ result = yield
144
+ elapsed = Time.now - start
145
+
146
+ print "[#{message}] " if message
147
+ puts "elapsed time: %0.5fs" % elapsed
148
+ result
149
+ end
150
+
151
+ #
152
+ # Quick and easy benchmark.
153
+ #
154
+ # Examples:
155
+ # bench { something }
156
+ # bench(90000000) { something }
157
+ # bench :fast => proc { fast_method }, :slow => proc { slow_method }
158
+ #
159
+ # In Ruby 1.9:
160
+ # bench fast: ->{ fast_method }, slow: ->{ slow_method }
161
+ #
162
+ def bench(*args, &block)
163
+
164
+ # Shitty perl-esque hack to let the method take all the different kind of arguments.
165
+ opts = Hash === args.last ? args.pop : {}
166
+ n = args.first || 100
167
+
168
+ if opts.any?
169
+
170
+ raise "Error: Supply either a do/end block, or procs as options. Not both." if block_given?
171
+ raise "Error: Options must be procs." unless opts.all? { |k, v| v.is_a?(Proc) }
172
+
173
+ benchblock = proc do |bm|
174
+ opts.each do |name, blk|
175
+ bm.report(name.to_s) { n.times &blk }
176
+ end
177
+ end
178
+
179
+ elsif block_given?
180
+
181
+ benchblock = proc do |bm|
182
+ bm.report { n.times &block }
183
+ end
184
+
185
+ else
186
+ raise "Error: Must supply some code to benchmark."
187
+ end
188
+
189
+ puts "* Benchmarking #{n} iterations..."
190
+ Benchmark.bmbm(&benchblock)
191
+ end
192
+
193
+ end
194
+
195
+
@@ -0,0 +1,338 @@
1
+ require 'epitools/core_ext/numbers'
2
+
3
+ class String
4
+
5
+ #
6
+ # Could this string be cast to an integer?
7
+ #
8
+ def integer?
9
+ strip.match(/^\d+$/) ? true : false
10
+ end
11
+
12
+ #
13
+ # 'true' if the string's length is 0 (after whitespace has been stripped from the ends)
14
+ #
15
+ def blank?
16
+ strip.size == 0
17
+ end
18
+
19
+ #
20
+ # Does this string contain something that means roughly "true"?
21
+ #
22
+ def truthy?
23
+ case strip.downcase
24
+ when "1", "true", "yes", "on", "enabled", "affirmative"
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ #
32
+ # Convert \r\n to \n
33
+ #
34
+ def to_unix
35
+ gsub("\r\n", "\n")
36
+ end
37
+
38
+ #
39
+ # Remove redundant whitespaces (not including newlines).
40
+ #
41
+ def tighten
42
+ gsub(/[\t ]+/,' ').strip
43
+ end
44
+
45
+ #
46
+ # Remove redundant whitespace AND newlines.
47
+ #
48
+ def dewhitespace
49
+ gsub(/\s+/,' ').strip
50
+ end
51
+
52
+ #
53
+ # Remove ANSI color codes.
54
+ #
55
+ def strip_color
56
+ gsub(/\e\[.*?(\d)+m/, '')
57
+ end
58
+ alias_method :strip_ansi, :strip_color
59
+
60
+ #
61
+ # Like #lines, but skips empty lines and removes \n's.
62
+ #
63
+ def nice_lines
64
+ # note: $/ is the platform's newline separator
65
+ split($/).select{|l| not l.blank? }
66
+ end
67
+
68
+ alias_method :nicelines, :nice_lines
69
+ alias_method :clean_lines, :nice_lines
70
+
71
+ #
72
+ # The Infamous Caesar-Cipher. Unbreakable to this day.
73
+ #
74
+ def rot13
75
+ tr('n-za-mN-ZA-M', 'a-zA-Z')
76
+ end
77
+
78
+ #
79
+ # Convert non-URI characters into %XXes.
80
+ #
81
+ def urlencode
82
+ URI.escape(self)
83
+ end
84
+
85
+ #
86
+ # Convert an URI's %XXes into regular characters.
87
+ #
88
+ def urldecode
89
+ URI.unescape(self)
90
+ end
91
+
92
+ #
93
+ # Convert a query string to a hash of params
94
+ #
95
+ def to_params
96
+ CGI.parse(self).map_values do |v|
97
+ # CGI.parse wraps every value in an array. Unwrap them!
98
+ if v.is_a?(Array) and v.size == 1
99
+ v.first
100
+ else
101
+ v
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ #
108
+ # Cached constants for base62 decoding.
109
+ #
110
+ BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.zip((0...Integer::BASE62_DIGITS.size).to_a) ]
111
+ BASE62_BASE = Integer::BASE62_BASE
112
+
113
+ #
114
+ # Convert a string (encoded in base16 "hex" -- for example, an MD5 or SHA1 hash)
115
+ # into "base62" format. (See Integer#to_base62 for more info.)
116
+ #
117
+ def to_base62
118
+ to_i(16).to_base62
119
+ end
120
+
121
+ #
122
+ # Convert a string encoded in base62 into an integer.
123
+ # (See Integer#to_base62 for more info.)
124
+ #
125
+ def from_base62
126
+ accumulator = 0
127
+ digits = chars.map { |c| BASE62_DIGITS[c] }.reverse
128
+ digits.each_with_index do |digit, power|
129
+ accumulator += (BASE62_BASE**power) * digit if digit > 0
130
+ end
131
+ accumulator
132
+ end
133
+
134
+ #
135
+ # Decode a mime64/base64 encoded string
136
+ #
137
+ def from_base64
138
+ Base64.decode64 self
139
+ end
140
+ alias_method :decode64, :from_base64
141
+
142
+ #
143
+ # Encode into a mime64/base64 string
144
+ #
145
+ def to_base64
146
+ Base64.encode64 self
147
+ end
148
+ alias_method :base64, :to_base64
149
+ alias_method :encode64, :to_base64
150
+
151
+ #
152
+ # MD5 the string
153
+ #
154
+ def md5
155
+ Digest::MD5.hexdigest self
156
+ end
157
+
158
+ #
159
+ # SHA1 the string
160
+ #
161
+ def sha1
162
+ Digest::SHA1.hexdigest self
163
+ end
164
+
165
+ #
166
+ # gzip the string
167
+ #
168
+ def gzip(level=nil)
169
+ zipped = StringIO.new
170
+ Zlib::GzipWriter.wrap(zipped, level) { |io| io.write(self) }
171
+ zipped.string
172
+ end
173
+
174
+ #
175
+ # gunzip the string
176
+ #
177
+ def gunzip
178
+ data = StringIO.new(self)
179
+ Zlib::GzipReader.new(data).read
180
+ end
181
+
182
+ #
183
+ # deflate the string
184
+ #
185
+ def deflate(level=nil)
186
+ Zlib::Deflate.deflate(self, level)
187
+ end
188
+
189
+ #
190
+ # inflate the string
191
+ #
192
+ def inflate
193
+ Zlib::Inflate.inflate(self)
194
+ end
195
+
196
+ # `true` if this string starts with the substring
197
+ #
198
+ def startswith(substring)
199
+ self[0...substring.size] == substring
200
+ end
201
+
202
+ #
203
+ # `true` if this string ends with the substring
204
+ #
205
+ def endswith(substring)
206
+ self[-substring.size..-1] == substring
207
+ end
208
+
209
+ #
210
+ # Parse this string as JSON
211
+ #
212
+ def from_json
213
+ JSON.parse(self)
214
+ end
215
+
216
+ #
217
+ # Parse this string as YAML
218
+ #
219
+ def from_yaml
220
+ YAML.parse(self).to_ruby
221
+ end
222
+
223
+ #
224
+ # Unmarshal the string (transform it into Ruby datatypes).
225
+ #
226
+ def unmarshal
227
+ Marshal.restore self
228
+ end
229
+ alias_method :from_marshal, :unmarshal
230
+
231
+ #
232
+ # Convert the string to a Path object.
233
+ #
234
+ def as_path
235
+ Path[self]
236
+ end
237
+ alias_method :to_p, :as_path
238
+
239
+ #
240
+ # Convert this string into a string describing this many of the string.
241
+ # (Note: Doesn't know anything about proper grammar.)
242
+ #
243
+ # Example:
244
+ # "cookie".amount(0) #=> "0 cookies"
245
+ # "shirt".amount(17) #=> "17 shirts"
246
+ # "dollar".amount(-10) #=> "-10 dollars"
247
+ # "love".amount(1) #=> "1 love"
248
+ #
249
+ def amount(n)
250
+ case n
251
+ when 0
252
+ "0 #{self}s"
253
+ when 1, -1
254
+ "#{n} #{self}"
255
+ else
256
+ "#{n} #{self}s"
257
+ end
258
+ end
259
+
260
+
261
+ unless public_method_defined? :to_proc
262
+
263
+ #
264
+ # String#to_proc
265
+ #
266
+ # See http://weblog.raganwald.com/2007/10/stringtoproc.html
267
+ #
268
+ # Ported from the String Lambdas in Oliver Steele's Functional Javascript
269
+ # http://osteele.com/sources/javascript/functional/
270
+ #
271
+ # This work is licensed under the MIT License:
272
+ #
273
+ # (c) 2007 Reginald Braithwaite
274
+ # Portions Copyright (c) 2006 Oliver Steele
275
+ #
276
+ # Permission is hereby granted, free of charge, to any person obtaining
277
+ # a copy of this software and associated documentation files (the
278
+ # "Software"), to deal in the Software without restriction, including
279
+ # without limitation the rights to use, copy, modify, merge, publish,
280
+ # distribute, sublicense, and/or sell copies of the Software, and to
281
+ # permit persons to whom the Software is furnished to do so, subject to
282
+ # the following conditions:
283
+ #
284
+ # The above copyright notice and this permission notice shall be
285
+ # included in all copies or substantial portions of the Software.
286
+ #
287
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
288
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
289
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
290
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
291
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
292
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
293
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
294
+ #
295
+ def to_proc &block
296
+ params = []
297
+ expr = self
298
+ sections = expr.split(/\s*->\s*/m)
299
+ if sections.length > 1 then
300
+ eval_block(sections.reverse!.inject { |e, p| "(Proc.new { |#{p.split(/\s/).join(', ')}| #{e} })" }, block)
301
+ elsif expr.match(/\b_\b/)
302
+ eval_block("Proc.new { |_| #{expr} }", block)
303
+ else
304
+ leftSection = expr.match(/^\s*(?:[+*\/%&|\^\.=<>\[]|!=)/m)
305
+ rightSection = expr.match(/[+\-*\/%&|\^\.=<>!]\s*$/m)
306
+ if leftSection || rightSection then
307
+ if (leftSection) then
308
+ params.push('$left')
309
+ expr = '$left' + expr
310
+ end
311
+ if (rightSection) then
312
+ params.push('$right')
313
+ expr = expr + '$right'
314
+ end
315
+ else
316
+ self.gsub(
317
+ /(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|self|arguments|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/, ''
318
+ ).scan(
319
+ /([a-z_$][a-z_$\d]*)/i
320
+ ) do |v|
321
+ params.push(v) unless params.include?(v)
322
+ end
323
+ end
324
+ eval_block("Proc.new { |#{params.join(', ')}| #{expr} }", block)
325
+ end
326
+ end
327
+
328
+ private
329
+
330
+ def eval_block(code, block)
331
+ eval code, block && block.binding
332
+ end
333
+
334
+ end # unless public_method_defined? :to_proc
335
+
336
+ end
337
+
338
+