Exploring Ruby Facets Gem


The ruby facets gem is packed with plenty of helpful methods which extend core ruby. As mentioned on the facets gem site, it is the single largest collection of core extensions aside from the standard library itself.

Before I go on our script contains the following lines in order to have some data to work with, as well as requiring the facets gem.

require 'rubygems'
require 'facets'

vehicles = %w[ car truck boat plane helicopter bike sea\ plane ]

All Possible Combinations

p vehicles.combination(2) { |v| p v }

outputs:

["car", "truck"]
["car", "boat"]
["car", "plane"]
["car", "helicopter"]
["car", "bike"]
["car", "sea plane"]
["truck", "boat"]
["truck", "plane"]
["truck", "helicopter"]
["truck", "bike"]
["truck", "sea plane"]
["boat", "plane"]
["boat", "helicopter"]
["boat", "bike"]
["boat", "sea plane"]
["plane", "helicopter"]
["plane", "bike"]
["plane", "sea plane"]
["helicopter", "bike"]
["helicopter", "sea plane"]
["bike", "sea plane"]

Irregular Join With Conjoin

p vehicles.conjoin(', ', :last => ' and a ')
# => "car, truck, boat, plane, helicopter, bike and a sea plane"

Deleting Unwanted Values

p vehicles.delete_unless { |v| v.length < 5 }
# => ["car", "boat", "bike"]

vehicles.delete_values('car', 'plane', 'bike', 'boat')
p vehicles
# => ["truck", "helicopter", "sea plane"]

Padding Arrays

p ['hello'].pad!(3, 'world')
# => ["hello", "world", "world"]

p ['hello'].pad!(-3, 'world')
# => ["world", "world", "hello"]

arr = ["a", ["b", ["d", nil, nil], "c", nil], nil]
p arr.recursively!{|a| a.compact.pad(3, 'pad') }
# => ["a", ["b", ["d", "pad", "pad"], "c"], "pad"]

Recursively Remove Nil Members

arr = ["a", ["b", ["d", nil, nil], "c", nil], nil]
p arr.recursively{|a| a.compact! }
# => ["a", ["b", ["d"], "c"]]

Traversing Members Recursively

a = ['a', 'b', ['c', 'd']]
p a.traverse! { |v| v.upcase }
#=> ["A", "B", ["C", "D"]]

Finding The Difference Of A Hash

one = {:one => 1, :two => 2, :three => { :four => 4 }}
two = {:one => 1, :three => { :four => 4 }}
p one.diff(two)
# => {:two=>2}

Joining Hash Key Value Pairs

one = {:one => 1, :two => 2, :three => { :four => 4 }}
p one.join(':', ', ')
# => "one:1, two:2, three:four4"

Rotating Arrays

This one may look a little confusing, but really it is simply unshifting, and pushing
values from the array, essentially 'rotating' it.

arr = [1, 2, 3, 4]

p arr.rotate!
# => [4, 1, 2, 3]

p arr.rotate!(2)
# => [2, 3, 4, 1]

p arr.rotate!(3)
# => [3, 4, 1, 2]

Hash + - & << and * operators

a = { :one => 1, :two => 2 }
b = { :one => 1, :three => 3, :four => 4 }

p a + b
# => {:three=>3, :four=>4, :one=>1, :two=>2}

p a - b
# => {:two=>2}

p a & b
# => {:one=>1}

p a * b # Reverse merge
# => {:three=>3, :four=>4, :one=>1, :two=>2}

p a << b
# => {:three=>3, :four=>4, :one=>1, :two=>2}

p a << ['key', 'value']
# => {:three=>3, :four=>4, :one=>1, "key"=>"value", :two=>2}

Splicing Members

p vehicles.splice 1
# => "truck"

vehicles.splice 1, 'new truck'
p vehicles
# => ["car", "new truck", "plane", "helicopter", "bike", "sea plane"]

Creating Instances In Bulk

The Class class implements the to_proc method so now you can create objects in bulk :)

class Vehicle
  attr_reader :name
 
  def initialize(name)
    @name = name
  end
end

p vehicles.collect(&Vehicle).collect{ |v| v.name }
# => ["car", "truck", "boat", "plane", "helicopter", "bike", "sea plane"]

Recursively Merge Hashes

a = {:a=>1, :b=>2, :c =>{:d => 2}}
b = {:b=>3, :a=>1, :c =>{:e => 3, :f => {:g => 5}}}
p a.recursive_merge(b)
# => {:a=>1, :b=>3, :c=>{:d=>2, :e=>3, :f=>{:g=>5}}}

Integer Ordinal

p 1.ordinal       # => 1st
p 8.ordinal       # => 8th
p 22.ordinal     # => 22nd
p 123.ordinal   # => 123rd

Getting Subclasses Of A Class

p String.subclasses
# => []

class SpecialString < String; end
class AnotherString < String; end

p String.subclasses
# => [SpecialString, AnotherString]

With Construct Similar To JavaScript

Some of you may be used to the 'with' construct within JavaScript allowing a block of code to be evaluated relative to a specific object, this is useful for readability, and can be done using instance_eval or 'with' in Ruby:

test = ['a', 'b', 'c']

with test do
  self << 'd'
  self << 'e'
  p length
end

p test

# => 5
# => ['a', 'b', 'c', 'd', 'e']

Finding Commonalities Between Members

p vehicles.commonality { |v| v.length }
# => {5=>["truck", "plane"], 4=>["boat", "bike"]}

p vehicles.commonality { |v| v[0..0] }
# => {"b"=>["boat", "bike"]}

Misc Methods

p (1..10).group_by { |n| n % 2 }
# => {0=>[2, 4, 6, 8, 10], 1=>[1, 3, 5, 7, 9]}

p vehicles.count('car')
# => 1

p ['a', 'b', 'e', 'a'].duplicates
# => ["a"]

p ['a', 'b', 'd', 'a', 'd', ['hey'], nil, nil].frequency
# => {"a"=>2, "b"=>1, nil=>2, "d"=>2, [nil]=>1}

p vehicles.accumulate.length.max
# => 10

p vehicles.map_send(:length).max
# => 10

p ['a', 'b', 'a', 'c', 'd'].mode
# => ["a"]

p vehicles.none? { |v| v == 'tank' }
# => true

p [1, 2, 3, 4, 5, 6, 7, 8].modulate(2)
p [1, 2, 3, 4, 5, 6, 7, 8].modulate(4)
# => [[1, 3, 5, 7], [2, 4, 6, 8]]
# => [[1, 5], [2, 6], [3, 7], [4, 8]]

p [1,1,2,4,4,5,6,6,6].occur(1)
p [1,1,2,4,4,5,6,6,6].occur(2)
p [1,1,2,4,4,5,6,6,6].occur(3)
# => [5, 2]
# => [1, 4]
# => [6]

p [1,1,1,2].probability
# => {1=>0.75, 2=>0.25}

p vehicles.not_empty?
# => true

p '   '.blank?
# => true