Well Grounded

Foundations

You must use return when returning multiple results from a method, or just return the array

def m
  return 1,2,3
end
 
print m  [1, 2, 3]
 
def m
  1,2,3
end
 
print m  [1, 2, 3]  syntax error, unexpected ',', expecting keyword_end

__send__ is an safer alternative to send if you are paranoid about somebody monkeypatching send.

Note: send can call an objects private methods. If may be safer to use public_send for 'tainted' send parameters.

Object.new.send(:local_variables)  [:_]
Object.new.public_send(:local_variables)  NoMethodError: private method `local_variables' called

Beware that setter methods will return the set value, not what the method returns, for consistency

class My
  def x=(x)
    @x = x
    'boooo' 
  end
  def x
    @x
  end
end
 
o = My.new
p o.x = 1   1, not 'boooo'

The default superclass of a class is Object

class MyClass
end
 
# same as
 
class MyClass < Object
end

You can make a class like this:

my_class = Class.new do
 
  def hello
    puts :hello
  end
 
end
 
my_class.new.hello  hello
Built in Essentials

to_a is related to the splat operator

the splat operator uses .to_a()
class MyClass
  def to_a
    ['hi''there'
  end
end
 
p *MyClass.new
 hi
 there

splat related cool trick for being sure to create an array

seems to_a is not required for a variable to respond to *
a = 1 # integer
p [*a]  [1]
a = 'asf' # string
p [*a]  ['asdf']
a = [1,2,3] # array
p [*a]  [1,2,3]

use Integer() to convert or validate an integer

i = Integer(1)
p i = Integer(1.623)  1
p i = Integer('number' invalid value for Integer(): "number" (ArgumentError

to_s is used by puts, etc

to_str is used when a string is required, like + and <<

class MyClass
  def to_str
    'instance of MyClass'
  end
end
 
puts 'this is an ' + MyClass.new  this is an instance of MyClass
Collections central: Enumerable and Enumerator

Danger of find()ing for nil

p [1,2,3,nil,4,5,6].find {|n| n.nil?}  nil
p [1,2,3,4,5,6].find {|n| n.nil?}  nil
 
# use include? instead
p [1,2,3,nil,4,5,6].include? nil  true
p [1,2,3,4,5,6].include? nil  false

select! / reject! is a handy but brutal

a = [1,2,3,4,5,6,7,8]
a.select! {|x| x % 2 == 1}
p a  [1,3,5,7]
 
# there's an opposite
a = [1,2,3,4,5,6,7,8]
a.reject! {|x| x % 2 == 1}
p a  [2,4,6,8]

grep and ===

grep calls === on argument for each element
same as enumerable.select {|element| expression === element }
p [1,2,"3",4,"5",6].grep String  ["3""5"]
p ['red''white''blue'].grep /w/  ['white]
p [10, 20, 30, 40, 50].grep 20..40  [20, 30, 40]
 
# grep also takes a block for mutations
 
p ['red''white''blue'].grep(/w/) {|x| x.upcase}  ['WHITE]
 
# or just do it like this with map and don't confuse everybody
p ['red''white''blue'].grep(/w/).map {|x| x.upcase}  ['WHITE]

take and drop look handy

a = [1,2,3]
p a.take(2)  [1,2]
p a  [1,2,3] (unchanged)
 
p a.drop(2)  [3]
p a  [1,2,3] (unchanged)

take_while and drop_while are like first and select

p [12,32,54,100,445,35353,333333].take_while {|x| x < 500}  [12, 32, 54, 100, 445]
p [12,32,54,100,445,35353,333333].drop_while {|x| x < 500}  [35353, 333333]

min_by is less confusing than min and the spaceship operator if you aren't using numbers

p %w{red blue green purple yellow}.min  blue
p %w{red blue green purple yellow}.min_by {|x| x.size }  red
p %w{red blue green purple yellow}.min_by {|x| x[0].ord }  blue

Three ways to sort an array of custom classes

# define a method <=> for things 
things.sort
 
# specify how to sort
things.sort {|a,b| a.value <=> b.value } 
 
# specify what to sort on
things.sort_by {|a| a.value }

Include Comparable, define <=>, and get comparison abilities for your class

class ComparableClass
  include Comparable
  attr_accessor :value
  def initialize(value)
    self.value = value
  end
  def <=>(other)
    value <=> other.value
  end
  def inspect
    value
  end
end
 
objs = 5.times.map {|x| ComparableClass.new(x) }
p objs[0] < objs[3]  true
p objs[2] < objs[1]  false

Enumerators can be implemented like below (enumerators depend on fibers ). I wrote this in 5 minutes but it still hurts my head to think about.

class MyEnumerator
 
  def initialize(&b)
 
    @fiber = Fiber.new do
      yielder = ->(x){ Fiber.yield x }
      b.call(yielder)
    end
 
  end
 
  def next
    @fiber.resume
  end
 
end
 
 
e = MyEnumerator.new do |y|
  [1,2,3].each do |x| 
    y.yield x # y.call(x) seems to work for custom enumerator, not for standard enumerator
  end
end
 
p e.next  1
p e.next  2
p e.next  3

enum_for takes a method to enumerate on, and could be implemented like this

class Array
  def my_enum_for(method = :each)
    Enumerator.new do |y|
      send(method) do |x|
        y << x
      end
    end
  end
end
 
e = [1,2,3].my_enum_for(:map)
 
p e.next  1
p e.next  2
p e.next  3

Protect an object with its enumerable (not sure how useful this is)

a = (0..9).to_a
e = a.enum_for
p e.map {|x| x}  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
e << 10  undefined method `<<' for Enumerator:

Making a class 'enumerable' without including Enumerable by hooking its each() up to enumerator

class MyClass
  def initialize
    @a = (0..9).to_a
  end
  def each
    @a.each {|x| yield x}
  end
end
 
obj = MyClass.new
obj.each {|x| print x }  0123456789
 
p obj.enum_for(:each).min  0
p obj.enum_for(:each).max  9
Regex

Three ways to test for regex match, ugh

re = /there/
string = 'here there everywhere'
 
puts "Match!" if re.match(string)
puts "Match!" if string =~ re
puts "Match!" if re =~ string # reverse of previous
puts "Match!" if re === string
puts "Match!" if string === re  doesn't work in reverse
Object Individualization

extend() : "the mixing of a module into an object's singleton class"

From ruby-doc.org: "Adds to obj the instance methods from each module given as a parameter."
Common use: dump a modules instance methods into a class's singleton class
Difference from include(): which dumps modules instance methods into classes instance methods

module MyModule
  def hello
    puts :hello
  end
end
 
obj = Object.new
obj.extend MyModule
obj.hello  hello
 
String.extend MyModule
'asdf'.class.hello  hello
 
# this would have worked too
class << obj
  include MyModule 
end
 
obj.hello  hello
Callable and Runnable Objects

Some idioms used used proc objects

# currying
def mult(factor)
  Proc.new do |x|
    x * factor
  end
end
mult3 = mult(3)
p mult3.call(3)  9
 
# counter
def mk_counter
  count = 0
  Proc.new { count = count + 1 }
end
 
counter = mk_counter
p counter.call  1
p counter.call  2
p counter.call  3

Weird way to run runable object

proc = Proc.new {|x| puts x }
 
# normal
proc.call('asdf' asdf
 
# not that weird
proc['asdf' asdf
 
# weird
proc.('asdf' asdf

instance_eval for more friendly configuration

class Person
 
  def initialize(&block)
    # the secret sauce - run block in context of self
    instance_eval(&block) 
  end
 
  # both setter and getter
  def first(name=nil)
    @first = @first || name
  end
 
  # both setter and getter
  def last(name=nil)
    @last = @last || name
  end
 
end
 
person = Person.new do
  first 'Bob'
  last 'Smith'
end
 
p person.first  Bob
p person.last  Smith
Callbacks, hooks, and runtime introspection

method_missing can be used for delegation

class MyArray
 
  def initialize
    @array = []
  end
 
  def method_missing(method, *args, &b)
    @array.send(method, *args, &b)
  end
 
end
 
ma = MyArray.new
ma << 1
ma << 2
ma << 3
p ma.join(', ' "1, 2, 3"

use included to mixin a class method with include()

could use extend() but sometimes you want to mixin *both* class and instance methods
module MyModule
 
  def a_instance_method
    puts 'hello from a_instance_method'
  end
 
  def self.included(c)
    def c.a_class_method
      puts 'hello from a_class_method'
    end
  end
 
end
 
class MyClass
  include MyModule
end
 
MyClass.a_class_method  hello from a_class_method
MyClass.new.a_instance_method  hello from a_instance_method

Note: Is it true there is no way to mixin module class methods?

Using include() we can include instance methods into a class.
Using extend() we can include instance methods into an objects singleton class.
There might be with unbind and bind, but its looking like a mess.