Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorators in Ruby (migrating from Python)

I'm spending today learning Ruby from a Python perspective. One thing I have completely failed to grapple with is an equivalent of decorators. To pare things down I'm trying to replicate a trivial Python decorator:

#! /usr/bin/env python  import math  def document(f):     def wrap(x):         print "I am going to square", x         f(x)     return wrap  @document def square(x):     print math.pow(x, 2)  square(5) 

Running this gives me:

I am going to square 5 25.0 

So, I want to create a function square(x), but decorate it so it alerts me as to what it's going to square before it does it. Let's get rid of the sugar to make it more basic:

... def square(x):     print math.pow(x, 2) square = document(square) ... 

So, how do I replicate this in Ruby? Here's my first attempt:

#! /usr/bin/env ruby  def document(f)     def wrap(x)         puts "I am going to square", x         f(x)         end     return wrap     end  def square(x)     puts x**2     end  square = document(square)  square(5) 

Running this generates:

./ruby_decorate.rb:8:in `document': wrong number of arguments (0 for 1) (ArgumentError)     from ./ruby_decorate.rb:15:in `<main>' 

Which I guess is because parentheses aren't mandatory and it's taking my return wrap as an attempt to return wrap(). I know of no way to refer to a function without calling it.

I've tried various other things, but nothing gets me far.

like image 732
jameshfisher Avatar asked Dec 11 '09 22:12

jameshfisher


People also ask

Is decorators can be chained in Python?

So, here in this post, we are going to learn about Decorator Chaining. Chaining decorators means applying more than one decorator inside a function. Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together.

What are decorators in Ruby?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

Did Python use decorators?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

Can decorators be async?

A decorator can be used to simply and cleanly implement retry functionality for asynchronous functions.


1 Answers

Here's another approach that eliminates the problem with conflicts between names of aliased methods (NOTE my other solution using modules for decoration is a good alternative too as it also avoids conflicts):

module Documenter     def document(func_name)            old_method = instance_method(func_name)           define_method(func_name) do |*args|                puts "about to call #{func_name}(#{args.join(', ')})"               old_method.bind(self).call(*args)           end     end end 

The above code works because the old_method local variable is kept alive in the new 'hello' method by fact of define_method block being a closure.

like image 50
horseyguy Avatar answered Oct 04 '22 06:10

horseyguy