Other ways to interact with an object

Usual way that you interact with an object inside your ruby code is by injecting the dependency (hopefully), but Ruby gives us some really powerful ways on how to interact with object in different ways thanks to procs and lambdas. These first order functions provide us with really flexible way to create a DSL, configuration class or do some work in the middle of method. Let's explore some of the options that we have:

Passing self to a block

First example is very straight forward and easy. We just pass "self" which represents the object in given class to the block that we are interacting with and call a methods on it as a regular object (In our example it is a class).
You will see this technique commonly used in configurations:

Configuration.config do |c|
  c.allow_ssl = true
  c.cert_path = "/var/www"
end

The goal here is to define class attributes that can be accessed trough out your application. We usually want to keep them in one place, that's why it is good idea to hold them as class attributes.

class Configuration
  class << self
    def config
      yield(self)
    end
    
    attr_accessor :allow_ssl, :cert_path
  end
end

By passing self to yield you are passing given object to the block and thus allowing to interact with it like a normal object.

Of course no one prohibits from using it inside of an instance. Let's try to implement something that can be ran and customised in the middle of method execution:

class InMiddleExample
  def in_middle_of_job
    do_before_job
    yield(self)
    do_after_job
  end
  
  def do_before_job
    puts "Before job"
  end
  
  def do_after_job
    puts "After job"
  end
  
  def some_other_work
    puts "Some other work"
  end
end

InMiddleExample.new.in_middle_of_job do |o|
  o.some_other_work
  puts "In the middle of job"
end

Sure enough, if you launch this code in your IRB console, your result will be:

Before job
Some other work
In the middle of job
After job
=> nil

Passing a lambda for delayed execution

I have covered this topic in my previous post: Callbacks and Ruby But this might be useful to recap (even for myself):

class LambdaExample
  def initialize(callback)
    @callback = callback
  end
  
  attr_reader :callback
  
  def say_hello_from_lambda
    callback.call("Hello")
  end
end

lambda_example = LambdaExample.new(
	->(greeting) do
    	puts "#{greeting} Janis"
    end
)

lambda_example.say_hello_from_lambda 

As you can see, execution is delayed until you call say_hello_from_lambda
which calls .call on the lambda and executes it. (Too much "call") :)

[12] pry(main)> lambda_example.say_hello_from_lambda 
Hello Janis
nil

Switching the context

We are used to calling method on objects like so object.method_call What if you want to switch context in order to call methods inside of other object using block? Thats actually pretty simple to do with instance_eval method which accepts blocks or strings of code to evaluate. This technique is commonly used inside of DSL's like Rspec with it's describe, it and context blocks

class InstanceEvalExample
  def switch_context(&block)
  	instance_eval(&block)
  end
  
  def say_hello_from_other_classes_context
  	puts "Hello"
  end
end

instance_eval_example = InstanceEvalExample.new

Now you can call this objects methods inside of the block.

[17] pry(main)> instance_eval_example.switch_context do
[17] pry(main)*   say_hello_from_other_classes_context  
[17] pry(main)* end  
Hello
nil

The reason it works that way is because instance eval ,for the time of execution of proc, is switching context or in other words switches self to given instance of InstanceEvalExample

Ruby allows us a lot of flexibility when dealing with objects and these are only three ways of doing that. I am pretty sure there are other ones, but these are most common ones. Hope you enjoyed.

Janis Miezitis

Read more posts by this author.

Subscribe to Janis Miezitis personal blog

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!