Skip to Content
Course content

90: Method Objects and UnboundMethod

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've treated methods as things you call. You send a message to an object, the method executes, and you get a result. But in Ruby, methods can be treated as first-class objects. This means you can grab a method, put it in a variable, pass it as an argument to another function, or even store it in an array. It's an incredibly powerful tool for building flexible plugins or dynamic dispatch systems.

Turning a method into a handle

Let's say we're building a simple PriceCalculator. Depending on the customer's region, we might want to apply different tax strategies. Instead of writing a giant if/else block every time we need to calculate a total, I want to be able to "pick" a method and pass it around.

class PriceCalculator
  def flat_tax(amount)
    amount + 5.0
  end

  def percentage_tax(amount)
    amount * 1.15
  end
end

calc = PriceCalculator.new
# Instead of calling the method, we capture it
tax_strategy = calc.method(:percentage_tax)

puts tax_strategy.call(100) # => 115.0

By calling method(:percentage_tax), I've created a Method object. Notice that I used .call(100) instead of just calling the method name. This tax_strategy variable now holds a reference to that specific method, bound to that specific calc instance.

The "Unbound" trap

Now, here is where things get a bit weirder. Sometimes you don't want a method tied to a specific instance; you want the definition of the method from the class itself. This is where instance_method comes in. I'll show you where I usually trip up when I first dive back into this.

I might think, "I don't have an instance of PriceCalculator yet, but I know I want to use the flat_tax logic." So, I try this:

# Attempting to grab the method from the class
generic_tax = PriceCalculator.instance_method(:flat_tax)

# I try to use it immediately...
generic_tax.call(100) 
# RuntimeError: UnboundMethod cannot be called without a target receiver

I just hit a wall. Why did that fail? Because instance_method returns an UnboundMethod. Think of it like a recipe that hasn't been assigned to a chef. The logic is there, but there's no self for the method to act upon. Since flat_tax is an instance method, it expects to belong to an object of PriceCalculator.

Binding the logic to an object

To fix this, we have to "bind" the UnboundMethod to an actual instance. This is useful when you have a pool of different objects and you want to apply the same piece of logic to all of them, regardless of how those objects were initialized.

calc_a = PriceCalculator.new
calc_b = PriceCalculator.new

# Grab the unbound method from the class
unbound_tax = PriceCalculator.instance_method(:flat_tax)

# Bind it to calc_a and call it
puts unbound_tax.bind(calc_a).call(100) # => 105.0

# Bind the exact same method object to calc_b
puts unbound_tax.bind(calc_b).call(100) # => 105.0

In this specific example, flat_tax doesn't use any internal state (like @tax_rate), so binding it to different instances doesn't change the result. However, if the method accessed self or an instance variable, .bind() would ensure the method operates on the data inside that specific object. It's a level of indirection that lets you separate the "what to do" from the "who to do it to."




📋 Practical Task

Building a Dynamic Data Sanitizer

Create a class called DataSanitizer with three methods: strip_whitespace, downcase_all, and remove_digits. Each method should take a string and return the modified version.

  • Create an instance of DataSanitizer.
  • Use the method method to capture strip_whitespace and remove_digits into an array called pipeline.
  • Given a dirty string (e.g., " User123 "), iterate through your pipeline array, calling each method object on the result of the previous one.
  • Finally, use DataSanitizer.instance_method(:downcase_all) to get an UnboundMethod, bind it to your sanitizer instance, and apply it to the final result.
  • Print the final cleaned string.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.