Ruby Advanced Guide

In this article, I will introduces some concepts of Ruby that not all Ruby developers know about

Screen Shot 2018-06-12 at 11.14.29 AM

Block

Is a way to group a short piece of code as a replacement of do … end. It’s not an object, so it cannot be passed as a parameter in a function.

[1, 2, 3].map { |e| e + 1 } # [2, 3, 4]

Proc

Proc is another way we use to shorten our code. Unlike Block, when using Proc, we instantiate an object, and then we can reuse that object in argument list as block

p = Proc.new { |e| e + 1 }
[1, 2, 3].map(&p) # [2, 3, 4]
p.object_id # 47417179317860
p.class # Proc

or in a method as a parameter

def proc_in_method arr, proc
  proc.call(arr)
end
proc = Proc.new { |arr| arr.sum }
proc_in_method [1, 2, 3], proc

Lambda

Lambda is a Proc object

A fun fact is that lambda is Proc object also. Let’s look

lam = lambda { |arr| arr.sum }
p lam.class # Proc

Lambda forces correct parameters

What’s the difference? lambda must have the correct parameters when calling while proc does not. Do you think we should use lambda instead proc?

lam = lambda { |arr| arr.sum }
p lam.call([1, 2, 3])
p lam.call()              # wrong number of arguments (given 0, expected 1)
p lam.call([1, 2, 3], []) # wrong number of arguments (given 2, expected 1)
proc = Proc.new { |name| puts "Hello #{name}" }
p proc.call('Colin', 'Dao') # Hello Colin

Lambda supports default argument

It sounds cool as a method, right?

lamb = lambda {|name = "Colin Dao"| puts "Hello #{name}"}
lamb.call()             # Hello Colin Dao
lamb.call("Ruby Guys")  # Hello Ruby Guys 

Lambda short form

Lambda has an amazed syntax

lamb = -> guest = "Ruby guys", name = "Colin Dao" { 
          puts "Hello #{guest}, I'm #{name}" 
        }
lamb.call()   # Hello Ruby guys, I'm Colin Dao

Lambda acts as closure

What’s closure? It means the accessibility of a function to another function, or local variables in the same scope but not inside that function.

Let’s look at below example

my_arr = [1, 2, 3, 4]

lamb = -> arr { arr.sum }
lamb.call my_arr

my_arr = [4, 5, 6]
lamb.call my_arr

Lambda & Scope

One of the common usage of lambda with Rails is for defining scope. Let’s look at below

Assume that you have an Employee model, and employee has position like: developer, team leader, project manager.

You might want to write

developers = Employee.where(position: 'developer').all
team_leaders = Employee.where(position: 'team_leader').all
project_managers = Employee.where(position: 'project_manager').all

Of course above code does the work. However, it seems not a good practice of writing code. Look at the second example

class Employee
  scope :position, -> position { where(position: position) }
end

# controllers
Employee.position('developer')
Employee.position('team_leader')
Employee.position('project_manager')

Inject / Reduce

Inject and Reduce methods are aliases. They can be used to combine all elements of enum by a binary operation.  Any below operations (being used with inject) can use reduce also

  result = (5..10).inject { |num1, num2| num1 + num2 }
  result # 45

You can add argument to inject, like below

  result = %w(one two three).inject({}) { |hash, el| hash[el] = el; hash }
  result # 45

  result = (5..10).inject(1) { |sum, num| sum + num }
  result # 46

  result = (5..10).inject { |num1, num2| num1 < num2 ? num1 : num2 }
  result # 45

  # Sum some numbers
  (5..10).inject(0, :+)    # 45
  # Multiply 
  (5..10).inject(1, :*)    # 151200

&:

It’s just a shortcut for block. Look at sample example and you see the magic

%w(a b c).map(&:capitalize)                          # ["A", "B", "C"]
%w(a b c).map { |character| character.capitalize }   # ["A", "B", "C"]

Yield

It’s magical, for sure. You can see it occurs a lot in Rails views. Please take a look at below example to see how yield often works

def try_yield
  p "start method"
  yield
  p "end method"
end

try_yield { puts "You see yield" }
# start method
# You see yield
# end method

try_yield do 
  puts "You see yield"
end

How if you want to use yield with params

def power(a,b)
    puts" #{a}^#{b} is"
    yield
end
power(a, b) { puts "#{a**b}" }
power(2,3)

In Rails, I think you will often see this

  <%= yield %>

or

  <%= yield :head %>
  <%= yield %>
  
  # and then 
  <% content_for :head do %>
    A simple page
  <% end %>
  Your html content

Hope you enjoy some above magic of Ruby!

Published by

Colin Dao

I am a hardworking Rubyist in Hanoi, Vietnam

Leave a comment