class Object

Constants

Enumerator
Inf
Infinity
Number
RbConfig
Slop

Public Class Methods

alias_class_method(dest, src) click to toggle source

Slightly gross hack to add a class method.

# File lib/epitools/minimal.rb, line 105
def self.alias_class_method(dest, src)
  metaclass.send(:alias_method, dest, src)
end
attrs(*names) click to toggle source

Creates attr_accessors and an initialize method that accepts the attrs as arguments. (It's kinda like an inline version of Struct.new(*args))

# File lib/epitools/core_ext/object.rb, line 55
def self.attrs(*names)
  attr_accessor *names
  define_method :initialize do |*vals|
    names.zip(vals) do |name, val|
      instance_variable_set("@#{name}", val)
    end
  end
end
enumerable(*meths) click to toggle source

Turns block-accepting iterator methods (eg: each) into methods that return an Enumerator when they're called called without a block.

It can transform many methods at once (eg: `enumerable :each, :all_people`).

Example:

def lots_of_stuff
  @stuff.each { |thing| yield thing }
end

enumerable :lots_of_stuff

Now you can use it like an Enumerator: object.lots_of_stuff.with_index.sort.zip(99..1000)

# File lib/epitools/minimal.rb, line 125
def self.enumerable *meths
  meths.each do |meth|
    alias_method "#{meth}_without_enumerator", meth
    class_eval %{
      def #{meth}(*args, &block)
        return to_enum(#{meth.inspect}, *args, &block) unless block_given?
        #{meth}_without_enumerator(*args, &block)
      end
    }
  end
end

Public Instance Methods

Path(arg) click to toggle source

Path(“/some/path”) is an alias for Path

# File lib/epitools/minimal.rb, line 280
def Path(arg)
  Path[arg]
end
Ratio(*args) click to toggle source

Function-style wrapper

# File lib/epitools/ratio.rb, line 76
def Ratio(*args)
  Ratio.new(*args)
end
TypedStruct(schema) click to toggle source
# File lib/epitools/typed_struct.rb, line 139
def TypedStruct(schema)
  TypedStruct[schema]
end
[](arg) click to toggle source

Slice the bits of an integer by passing a range (eg: 1217389172842 #=> [0, 1, 0, 1, 0, 1])

# File lib/epitools/core_ext/numbers.rb, line 414
def [](arg)
  case arg
  when Integer
    bit(arg)
  when Range
    bits[arg]
  end
end
__DIR__(*args) click to toggle source

This method is convenience for the `File.expand_path(File.dirname(__FILE__))` idiom. (taken from Michael Fellinger's Ramaze… thanx, dood! :D)

# File lib/epitools/minimal.rb, line 27
def __DIR__(*args)
  filename = caller[0][/^(.*):/, 1]
  dir = File.expand_path(File.dirname(filename))
  ::File.expand_path(::File.join(dir, *args.map{|a| a.to_s}))
end
ancestors() click to toggle source
# File lib/epitools/minimal.rb, line 178
def ancestors
  self.class.ancestors
end
autoinstall(*packages) click to toggle source

Automatically install a required commandline tool using the platform's package manager (incomplete – only supports debian :)

  • apt-get on debian/ubuntu

  • yum on fedora

  • fink/ports on OSX

  • cygwin on windows

# File lib/epitools/clitools.rb, line 155
def autoinstall(*packages)
  all_packages_installed = packages.all? { |pkg| Path.which pkg }

  unless all_packages_installed
    cmd(["sudo apt-get install ?", packages.join(' ')])
  end
end
autoreq(const, path=nil, &block) click to toggle source

'autoreq' is a replacement for autoload that can load gems.

Usage:

autoreq :Constant, 'thing-to-require'
autoreq :Constant, 'thing-to-require'
autoreq :OtherConstant do
  gem 'somegem', '~> 1.2'
  require 'somegem'
end
# File lib/epitools/minimal.rb, line 45
def autoreq(const, path=nil, &block)
  raise "Error: autoreq must be supplied with a file to load, or a block." unless !!path ^ block_given?

  if block_given?
    Module.autoreqs[const] = block
  else
    Module.autoreqs[const] = path
  end
end
bench(*args, &block) click to toggle source

Quick and easy benchmark.

Examples:

bench { something }
bench(90000000) { something }
bench :fast => proc { fast_method }, :slow => proc { slow_method }

In Ruby 1.9:

bench fast: ->{ fast_method }, slow: ->{ slow_method }
# File lib/epitools/core_ext/object.rb, line 124
def bench(*args, &block)

  # Shitty perl-esque hack to let the method take all the different kind of arguments.
  opts  = Hash === args.last ? args.pop : {}
  n     = args.first || 100

  if opts.any?

    raise "Error: Supply either a do/end block, or procs as options. Not both." if block_given?
    raise "Error: Options must be procs." unless opts.all? { |k, v| v.is_a?(Proc) }

    benchblock = proc do |bm|
      opts.each do |name, blk|
        bm.report(name.to_s) { n.times &blk }
      end
    end

  elsif block_given?

    benchblock = proc do |bm|
      bm.report { n.times &block }
    end

  else
    raise "Error: Must supply some code to benchmark."
  end

  puts "* Benchmarking #{n} iterations..."
  Benchmark.bmbm(&benchblock)
end
big_endian_bytes() click to toggle source
# File lib/epitools/core_ext/numbers.rb, line 436
def big_endian_bytes
  bytes.reverse
end
bytes() click to toggle source

Convert the integer into its sequence of bytes (little endian format: lowest-order-byte first)

TODO: This could be made much more efficient!

# File lib/epitools/core_ext/numbers.rb, line 428
def bytes
  nbytes = (bit_length / 8.0).ceil

  (0..nbytes).map do |current_byte|
    (self >> (8 * current_byte)) & 0xFF
  end
end
cached_curl(url) click to toggle source
# File lib/epitools/clitools.rb, line 251
def cached_curl(url)
  cache = "/tmp/curl-#{url.sha1}.cache"
  if File.exist?(cache)
    $stderr.puts "cached! => #{cache}"
  else
    File.write(cache, curl(url))
  end
  File.read(cache)
end
class_def(name, &blk) click to toggle source

Defines an instance method within a class

# File lib/epitools/minimal.rb, line 98
def class_def name, &blk
  class_eval { define_method name, &blk }
end
cmd(*args) click to toggle source

Execute a `system()` command using SQL-style escaped arguments.

Example:

cmd( ["cp -arv ? ?", "/usr/src", "/home/you/A Folder/dest"] )

Which is equivalent to:

system( "cp", "-arv", "/usr/src", "/home/you/A Folder/dest" )

Notice that you don't need to shell-escape anything. That's done automagically!

If you don't pass any arrays, `cmd` works the same as `system`:

cmd( "cp", "-arv", "etc", "etc" )
# File lib/epitools/clitools.rb, line 74
def cmd(*args)

  cmd_args = []

  for arg in args

    case arg

      when Array
        cmd_literals = arg.shift.split(/\s+/)

        for cmd_literal in cmd_literals
          if cmd_literal == "?"
            raise "Not enough substitution arguments" unless cmd_args.any?
            cmd_args << arg.shift
          else
            cmd_args << cmd_literal
          end
        end

        raise "More parameters than ?'s in cmd string" if arg.any?

      when String
        cmd_args << arg

      else
        cmd_args << arg.to_s

    end

  end

  p [:cmd_args, cmd_args] if $DEBUG

  system(*cmd_args)
end
color_backtrace_1(lines) click to toggle source
# File lib/epitools/pretty_backtrace.rb, line 54
def color_backtrace_1(lines)
  groups = lines.split_before { |line,nextline| line.path != nextline.path }
  for group in groups
    dir, filename = File.split(group.first.path)
    puts "#{filename.green} (#{dir.light_white})"
    # /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb
    #      234: require | 553: new_constants_in |
    group.each do |line|
      puts "  |_ #{line.num.magenta}: #{line.meth.light_yellow.underline}"
    end
    #puts "  |_ " + group.map{|line| "#{line.num.magenta}: #{line.meth.light_yellow}"}.join(' | '.red)
  end
end
color_backtrace_2(lines, options={}) click to toggle source
# File lib/epitools/pretty_backtrace.rb, line 70
def color_backtrace_2(lines, options={})

  groups = lines.reverse.split_at { |line,nextline| line.path != nextline.path }

  if options[:no_gems]
    groups = groups.split_when { |a, nexta| a.first.gem? != nexta.first.gem? }
    groups.map! { |group| group.first.gem? ? [] : group }
  end


  for group in groups
    if group.empty?
      puts " ... ignored ... "
      puts
      next
    end

    firstline = group.first

    # custom_require.rb (/usr/local/lib/site_ruby/1.8/rubygems)
    #      234: require          => super
    #      553: new_constants_in =>

    #puts "#{firstline.filename.green} (#{firstline.dir.light_white})"
    puts "#{firstline.filename.underline} (#{firstline.dir.light_white})"

    max_methsize = group.map { |line| line.meth.size}.max
    group.each do |line|
      pad = " " * (max_methsize - line.meth.size)
      puts "  #{line.num.magenta}: #{line.meth.light_yellow}#{pad}"
      puts "    #{"|_".blue} #{line.codeline.strip}"
    end
    puts
  end

end
curl(url) click to toggle source
# File lib/epitools/clitools.rb, line 233
def curl(url)
  curl_open(url).read
end
curl_json(url) click to toggle source
# File lib/epitools/clitools.rb, line 247
def curl_json(url)
  JSON.parse(curl(url))
end
curl_open(url, headers={}) click to toggle source
# File lib/epitools/clitools.rb, line 237
def curl_open(url, headers={})
  # headers["User-Agent"] ||= "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/85 Version/11.1.1 Safari/605.1.15"
  cmd = ["curl", "-LSs"]
  headers.each { |k,v| cmd += ["-H", "#{k}: #{v}"] }
  cmd << url
  IO.popen(cmd)
rescue Errno::ENOENT
  raise "Error: 'curl' isn't installed."
end
debug_backtrace(lines) click to toggle source
# File lib/epitools/pretty_backtrace.rb, line 120
def debug_backtrace(lines)
  lines.each do |line|
    p line.path
  end
end
del(x) click to toggle source

Remove an object, method, constant, etc.

# File lib/epitools/minimal.rb, line 58
def del(x)
  case x
    when String
      del(x.to_sym)
    when Class, Module
      Object.send(:remove_const, x.name)
    when Method
      x.owner.send(:undef_method, x.name)
    when Symbol
      if Object.const_get(x)
        Object.send(:remove_const, x)
      elsif method(x)
        undef_method x
      end
    else
      raise "Error: don't know how to 'del #{x.inspect}'"
  end
end
dmsg(msg) click to toggle source

Emit a quick debug message (only if $DEBUG is true)

# File lib/epitools/core_ext/object.rb, line 89
def dmsg(msg)
  if $DEBUG
    case msg
    when String
      puts msg
    else
      puts msg.inspect
    end
  end
end
float?() click to toggle source

Default “float?” behaviour.

# File lib/epitools/core_ext/truthiness.rb, line 12
def float?; false; end
geoip(addr, city_data='/usr/share/GeoIP/GeoIPCity.dat', country_data='/usr/share/GeoIP/GeoIP.dat') click to toggle source

Lookup GeoIP information (city, state, country, etc.) for an IP address or hostname

(Note: Must be able to find one of /usr/share/GeoIP/GeoIP{,City}.dat, or specified manually

as (an) extra argument(s).)
# File lib/epitools/clitools.rb, line 179
def geoip(addr, city_data='/usr/share/GeoIP/GeoIPCity.dat', country_data='/usr/share/GeoIP/GeoIP.dat')
  (
    $geoip ||= begin
      if city_data and File.exists? city_data
        geo = GeoIP.new city_data
        proc { |addr| geo.city(addr) }

      elsif country_data and File.exists? country_data
        geo = GeoIP.new country_data
        proc { |addr| geo.country(addr) }

      else
        raise "Can't find GeoIP data files."
      end
    end
  ).call(addr)
end
in?(enum) click to toggle source

Instead of:

if cookie_jar.include? cookie

Now you can do:

if cookie.in? cookie_jar
# File lib/epitools/minimal.rb, line 144
def in?(enum)
  enum.include? self
end
integer?() click to toggle source

Default “integer?” behaviour.

# File lib/epitools/core_ext/truthiness.rb, line 7
def integer?; false; end
lesspipe(*args) { |less| ... } click to toggle source

Create scrollable output via less!

This command runs `less` in a subprocess, and gives you the IO to its STDIN pipe so that you can communicate with it.

Example:

lesspipe do |less|
  50.times { less.puts "Hi mom!" }
end

The default less parameters are:

  • Allow colour

  • Don't wrap lines longer than the screen

  • Quit immediately (without paging) if there's less than one screen of text.

You can change these options by passing a hash to `lesspipe`, like so:

lesspipe(:wrap=>false) { |less| less.puts essay.to_s }

It accepts the following boolean options:

:color  => Allow ANSI colour codes?
:wrap   => Wrap long lines?
:always => Always page, even if there's less than one page of text?
# File lib/epitools/clitools.rb, line 27
def lesspipe(*args)
  if args.any? and args.last.is_a?(Hash)
    options = args.pop
  else
    options = {}
  end

  output = args.first if args.any?

  params = []
  params << "-R" unless options[:color] == false
  params << "-S" unless options[:wrap] == true
  params << "-F" unless options[:always] == true
  if options[:tail] == true
    params << "+\\>"
    $stderr.puts "Seeking to end of stream..."
  end
  params << "-X"

  IO.popen("less #{params * ' '}", "w") do |less|
    if output
      less.puts output
    else
      yield less
    end
  end

rescue Errno::EPIPE, Interrupt
  # less just quit -- eat the exception.
end
locals() click to toggle source

Return a hash of local variables in the caller's scope: {:variable_name=>value}

# File lib/epitools/core_ext/object.rb, line 6
def locals
  require 'binding_of_caller'
  caller = binding.of_caller(1)
  vars = caller.eval("local_variables").reject{|e| e[/^_/]}
  vals = caller.eval("[ #{vars.join(",")} ]")
  Hash[ vars.zip(vals) ]
end
longest_common_prefix(strings) click to toggle source

Return the longest common prefix between two strings.

# File lib/epitools/lcs.rb, line 2
def longest_common_prefix(strings)
  p = nil
  strings.map{|s|s.size}.min.times do |c|
    if strings.map{|s|s[c]}.uniq.size == 1
      p = c
    else
      break
    end
  end

  strings.first[0..p] unless p.nil?
end
longest_common_subsequence(s1, s2) click to toggle source
# File lib/epitools/lcs.rb, line 16
def longest_common_subsequence(s1, s2)

  num = Array.new(s1.size) { Array.new(s2.size) }
  len, ans = 0

  s1.chars.each_with_index do |l1, i|
    s2.chars.each_with_index do |l2, j|
      unless l1==l2
        num[i][j]=0
      else
        if (i==0 || j==0)
          num[i][j] = 1
        else
          num[i][j] = 1 + num[i-1][j-1]
        end
        len = ans = num[i][j] if num[i][j] > len
      end
    end
  end

  ans

end
marshal() click to toggle source

Serialize this object to a binary String, using Marshal.dump.

# File lib/epitools/core_ext/object.rb, line 67
def marshal
  Marshal.dump self
end
Also aliased as: to_marshal
meta_def(name, &blk) click to toggle source

Adds methods to a metaclass

# File lib/epitools/minimal.rb, line 93
def meta_def name, &blk
  meta_eval { define_method name, &blk }
end
meta_eval(&blk) click to toggle source
# File lib/epitools/minimal.rb, line 88
def meta_eval &blk
  metaclass.instance_eval &blk
end
metaclass() click to toggle source
# File lib/epitools/minimal.rb, line 81
def metaclass
  class << self
    self
  end
end
niceprint(o, level=0, indent_first_line=true) click to toggle source
# File lib/epitools/niceprint.rb, line 1
def niceprint(o, level=0, indent_first_line=true)
  maxstring = 50
  maxarray = 20

  result = ""

  dent = "    "
  indent = dent * level

  result << indent if indent_first_line

  case o

    when Hash
      #puts "Hash!"
      result << "{\n"

      o.each_with_index do |(k,v),i|
        result << "#{indent+dent}#{k.inspect} => #{niceprint(v,level+1,false)}"
        result << "," unless i == o.size
        result << "\n"
      end

      result << "#{indent}}"

    when Array
      #puts "Array!"
      indent_first = o.any? { |e| e.instance_of? Hash }

      if indent_first
        result << "[\n"
      else
        result << "["
      end

      o = o[0..maxarray] if o.size > maxarray
      o.each do |e|
        result << niceprint(e,level+1,indent_first)
        result << ", "
      end

      result << "]"

    when String
      #puts "String!"
      o = o[0..maxstring] + "..." if o.size > maxstring
      result << o.inspect

    else
      result << o.inspect
  end

  if level == 0
    print result
  else
    result
  end

end
not() click to toggle source

Negates a boolean, chained-method style.

Example:

>> 10.even?
=> true
>> 10.not.even?
=> false
# File lib/epitools/core_ext/object.rb, line 169
def not
  NotWrapper.new(self)
end
notify_send(title, body=nil, icon: nil, time: 5) click to toggle source

Executes notify-send to create a desktop notification on the user's desktop

Note: the 'icon' argument is the path to an image file

# File lib/epitools/clitools.rb, line 217
def notify_send(title, body=nil, icon: nil, time: 5)
  $stderr.puts "* #{title}"
  $stderr.puts "  |_ #{body}" if body

  time_in_ms = time * 1000

  cmd = ["notify-send"]
  cmd << "--expire-time=#{time_in_ms}"
  cmd << "--app-name=#{Process.argv0}"
  cmd << "--icon=#{icon}" if icon
  cmd << CGI.escapeHTML(title)
  cmd << CGI.escapeHTML(body) if body

  fork { system *cmd }
end
number?() click to toggle source

Default “number?” behaviour.

# File lib/epitools/core_ext/truthiness.rb, line 17
def number?; false; end
parse_lines(backtrace) click to toggle source
# File lib/epitools/pretty_backtrace.rb, line 38
def parse_lines(backtrace)
  backtrace.map do |line|
    case line
      when /^\s+(.+):(\d+):in \`(.+)'/
        Line.new($1, $2, $3)
      when /^\s+(.+):(\d+)/
        Line.new($1, $2, '')
      when /^\s+$/
        next
      else
        raise "huh?!"
    end
  end.compact
end
perms(size, n=0, stack=[]) { |n| ... } click to toggle source

Returns all the `size`-sized selections of the elements from an array.

I can't remember why I wrote it like this, but the array you want to permute is passed in as a block. For example:

>> perms(1) { [1,2,3,4] }
=> [[1], [2], [3], [4]]
>> perms(2) { [1,2,3,4] }
=> [[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4],
    [3, 1], [3, 2], [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]]

The block also gets passed a parameter: the depth of the recursion. I can't remember why I did that either! :D

# File lib/epitools/permutations.rb, line 66
def perms(size, n=0, stack=[], &block)
  ps = yield(n)
  results = []
  if n >= size
    results << stack
  else
    ps.each do |p|
      results += perms(size, n+1, stack + [p], &block)
    end
  end
  results
end
present?() click to toggle source
# File lib/epitools/core_ext/object.rb, line 155
def present?
  true
end
primes() click to toggle source

Return an infinite enumerable of primes

# File lib/epitools/core_ext/numbers.rb, line 485
def primes
  Prime.instance
end
prompt(message="Are you sure?", options="Yn") click to toggle source

Prompt the user for confirmation.

Usage:

prompt("Do you want a cookie?", "Ynqa") #=> returns the letter that the user pressed, in lowercase (and returns the default, 'y', if the user hits ENTER)
# File lib/epitools/clitools.rb, line 118
def prompt(message="Are you sure?", options="Yn")
  opts      = options.scan(/./)
  optstring = opts.join("/") # case maintained
  defaults  = opts.select{|o| o.upcase == o }
  opts      = opts.map{|o| o.downcase}

  raise "Error: Too many default values for the prompt: #{default.inspect}" if defaults.size > 1

  default = defaults.first

  loop do

    print "#{message} (#{optstring}) "

    response = STDIN.gets.strip.downcase

    case response
    when *opts
      return response
    when ""
      return default.downcase
    else
      puts "  |_ Invalid option: #{response.inspect}. Try again."
    end

  end
end
python_backtrace(lines) click to toggle source
# File lib/epitools/pretty_backtrace.rb, line 108
def python_backtrace(lines)
  #groups = lines.reverse.split_when { |line,nextline| line.path != nextline.path }
  lines = lines.reverse

  puts "Traceback (most recent call last):"

  for line in lines
    puts %{  File "#{line.path}", line #{line.num}, in #{line.meth}}
    puts %{    #{line.codeline.strip}}
  end
end
self() { |self| ... } click to toggle source

Return this object. If given a block, yields this object and returns the result of the block.

eg: stuff.group_by(&:self)

# File lib/epitools/minimal.rb, line 170
def self 
  if block_given?
    yield self
  else
    self
  end
end
sudoifnotroot() click to toggle source

Re-execute the script using sudo if it's not currently running as root.

# File lib/epitools/clitools.rb, line 167
def sudoifnotroot
  unless Process.uid == 0
    exit system("sudo", $PROGRAM_NAME, *ARGV)
  end
end
time(message=nil) { || ... } click to toggle source

Time a block.

# File lib/epitools/core_ext/object.rb, line 103
def time(message=nil)
  start = Time.now
  result = yield
  elapsed = Time.now - start

  print "[#{message}] " if message
  puts "elapsed time: %0.5fs" % elapsed
  result
end
to_bencode() click to toggle source

Serialize this object to a Python bencoded (pickled) string

# File lib/epitools/core_ext/object.rb, line 176
def to_bencode
  BEncode.dump(self)
end
to_json(pretty=true) click to toggle source

Serialize this object to JSON (defaults to “pretty” indented JSON).

# File lib/epitools/core_ext/object.rb, line 82
def to_json(pretty=true)
  pretty ? JSON::pretty_generate(self) : JSON.dump(self)
end
to_marshal()
Alias for: marshal
to_yaml() click to toggle source

Serialize this object to YAML.

# File lib/epitools/core_ext/object.rb, line 75
def to_yaml
  YAML::dump(self)
end
truthy?() click to toggle source

`truthy?` means `not blank?`

# File lib/epitools/core_ext/truthiness.rb, line 22
def truthy?
  if respond_to? :blank?
    not blank?
  else
    not nil?
  end
end
try(method, *args, &block) click to toggle source

Instead of:

@person ? @person.name : nil

Now you can do:

@person.try(:name)
# File lib/epitools/minimal.rb, line 160
def try(method, *args, &block)
  send(method, *args, &block) if respond_to? method
end
which(*bins) click to toggle source

Search the PATH environment variable for binaries, returning the first one that exists

# File lib/epitools/clitools.rb, line 201
def which(*bins)
  bins.flatten.each do |bin|
    ENV["PATH"].split(":").each do |dir|
      full_path = File.join(dir, bin)
      return full_path if File.exists? full_path
    end
  end
  nil
end
with(options={}) { |self| ... } click to toggle source

Gives you a copy of the object with its attributes changed to whatever was passed in the options hash.

Example:

>> cookie = Cookie.new(:size=>10, :chips=>200)
=> #<Cookie:0xffffffe @size=10, @chips=200>
>> cookie.with(:chips=>50)
=> #<Cookie:0xfffffff @size=10, @chips=50>

(All this method does is dup the object, then call “key=(value)” for each key/value in the options hash.)

BONUS FEATURE! ==

If you supply a block, it just gives you the object, and returns whatever your block returns.

Example:

>> {a: 10, b: 2}.with { |hash| hash[:a] / hash[:b] }
=> 5

Good for chaining lots of operations together in a REPL.

# File lib/epitools/core_ext/object.rb, line 39
def with(options={})
  if block_given?
    yield self
  else
    obj = dup
    options.each { |key, value| obj.send "#{key}=", value }
    obj
  end
end