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.
@@ -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
+