Ruby Snippets

A small collection of handy one-liners and useful snippets that I've written or collected for Ruby & Ruby on Rails.

Sum & Product of an Array

How to sum all the numbers in an array, or get the multiplication product of the same numbers, without resorting to an ugly for loop? Ruby's inject to the rescue; this snippet extends the array class with the methods sum and product, which can hereafter be used on any array in Ruby. Note that the parameter to inject is the initial value of s, the result, and thus obviously different depending on whether we're doing addition or multiplication.

class Array
  def sum;     inject(0) { |s, v| s += v }; end
  def product; inject(1) { |s, v| s *= v }; end
end

[1, 1, 2, 3, 5, 8].sum     => 20
[1, 1, 2, 3, 5, 8].product => 240

Generate Random Password

Generates a secure, entirely random password of the specified length (by default, 16 characters long). Note that the generated password doesn't include similar characters (e.g. i, l, o, 1, 0, I), facilitating reading it back correctly when written down on paper. This is a straightforward translation of my PHP version -- I'll need to rewrite it in a more Rubyesque style at some point.

def generate_passwd(length=16)
  chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789'
  password = ''
  length.downto(1) { |i| password << chars[rand(chars.length - 1)] }
  password
end

generate_passwd() => "3HGkQcOdrH2kTBAn"
generate_passwd() => "H7PpTEUeEwfuyAGH"

Useful Regular Expressions

This still needs some polishing, but useful nonetheless. The original version of these regexes came from RubyGarden.

class String
  NUM = /[-+]?(\d+\.)?\d+([eE][-+]?\d+)?/
  DEC = /[-+]?(0d\d([\d_]*\d)?)|([1-9]([\d_]*\d)?)|0/
  BIN = /[-+]?[01]([01_]*[01])?/
  OCT = /[-+]?0([0-7_]*[0-7])?/
  HEX = /[-+]?0x[\da-fA-F]([\da-fA-F_]*[\da-fA-F])?/

  def number?;  (self =~ NUM) != nil; end
  def decimal?; (self =~ DEC) != nil; end
  def binary?;  (self =~ BIN) != nil; end
  def octal?;   (self =~ OCT) != nil; end
  def hex?;     (self =~ HEX) != nil; end
end

Simple RSS Parsing

A simple method I've used to return the latest n items from a specified RSS feed, using only the libraries built into Ruby.

def fetch_rss_items(url, max_items = nil)
  %w{open-uri rss/0.9 rss/1.0 rss/2.0 rss/parser}.each do |lib|
    require(lib)
  end
  rss = RSS::Parser.parse(open(url).read)
  rss.items[0...(max_items ? max_items : rss.items.length)]
end

items = fetch_rss_items('http://www.digg.com/rss/index.xml', 5)
items.collect { |item| item.title }
=> ["Understanding AJAX - A Beginner's Guide",
    "Anti-cancer Compound In Beer", ...]

Determine Image Size

Credits to Sam Stephenson for this handy one-liner, which can be used to quickly determine the width and height of a PNG image file:

def get_png_size(filename)
  IO.read(filename)[0x10..0x18].unpack('NN')
end

get_png_size('image.png') => [640, 480]

Factorial Numbers

This snippet extends the integer type, giving all integers the ability to factorial themselves. Note that I've included both recursive and iterative versions: while the recursive method is arguably cleaner, for larger factorials you might run into a SystemStackError: stack level too deep error in Ruby; in that case, you'll want to use the iterative version.

class Integer
  def factorial_recursive
    self <= 1 ? 1 : self * (self - 1).factorial
  end
  def factorial_iterative
    f = 1; for i in 1..self; f *= i; end; f
  end
  alias :factorial, :factorial_recursive
end

(0..9).collect { |n| n.factorial }
=> [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

Ordinals for All Numeric Types

This snippet will extend all numerical types with a method for returning the English ordinal, i.e. 1st, 2nd, 3rd, 4th, etc. Note that floating-point types will suffer an implicit integer coercion without rounding. The method is based on this page, where you can find similar code in short-hand style as well (not that rewriting the following in ternary logic should prove an unsurmountable problem for anyone).

class Numeric
  def ordinal
    cardinal = self.to_i.abs
    if (10...20).include?(cardinal) then
      cardinal.to_s << 'th'
    else
      cardinal.to_s << %w{th st nd rd th th th th th th}[cardinal % 10]
    end
  end
end

[1, 22, 123, 10, -3.1415].collect { |i| i.ordinal }
=> ["1st", "22nd", "123rd", "10th", "3rd"]