In this article, I will introduces some concepts of Ruby that not all Ruby developers know about
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!