Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing code for every method call in a Ruby module

I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.

module MyModule   def go_forth     a re-used statement     # code particular to this method follows ...   end    def and_multiply     a re-used statement     # then something completely different ...   end end 

But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?

(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)

like image 600
GladstoneKeep Avatar asked Apr 01 '11 12:04

GladstoneKeep


People also ask

How do you call a module method in Ruby?

As with class methods, you call a module method by preceding its name with the module's name and a period, and you reference a constant using the module name and two colons.

What happens when you call a method in Ruby?

In ruby, the concept of object orientation takes its roots from Smalltalk. Basically, when you call a method, you are sending that object a message. So, it makes sense that when you want to dynamically call a method on an object, the method you call is send . This method has existed in ruby since at least 1.8.

How do you access a module method in Ruby?

A user cannot access instance method directly with the use of the dot operator as he cannot make the instance of the module. To access the instance method defined inside the module, the user has to include the module inside a class and then use the class instance to access that method.


2 Answers

Like this:

module M   def self.before(*names)     names.each do |name|       m = instance_method(name)       define_method(name) do |*args, &block|           yield         m.bind(self).(*args, &block)       end     end   end end  module M   def hello     puts "yo"   end    def bye     puts "bum"   end    before(*instance_methods) { puts "start" } end  class C   include M end  C.new.bye #=> "start" "bum" C.new.hello #=> "start" "yo" 
like image 92
horseyguy Avatar answered Oct 05 '22 19:10

horseyguy


This is exactly what aspector is created for.

With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.

require 'aspector'  module MyModule   aspector do     before :go_forth, :add_multiply do       ...     end   end    def go_forth     # code particular to this method follows ...   end    def and_multiply     # then something completely different ...   end end 
like image 42
Guoliang Cao Avatar answered Oct 05 '22 19:10

Guoliang Cao