1

Often, I see stuff like this:

class Someclass
 do_something_to :foo, :bar, :class_level

 def foo
   puts "Hi!"
 end

 def bar
 end

 def self.class_level
  puts "Something else!"
 end
end

I want to patch Class or Object to have my own version of do_something_to. Doesn't need to do anything fancy, a simple printout of the current time vs time at the beginning and end will do.

Here's a solution that works, except that it doesn't address class-level methods:

require 'aspector'

class Class
  def do_something_to(*names)
    aspector do
      around(names) do |proxy, *args, &block|
        start_time = Time.now.to_f
        proxy.call *args, &block
        final_time = Time.now.to_f - start_time 
        puts final_time
        return final_time
      end
    end
  end
end

class MyClass

  do_something_to :hey, :derp

  def hey
    puts "Hey!"
    sleep 1
  end
  def derp x
    puts x
    sleep 2
  end
end
dsp_099
  • 5,801
  • 17
  • 72
  • 128
  • Are you wanting to wrap your function around all methods (so any method call, regardless of what it is, prints the timestamps), or just one specific method (e.g. any class with a `test` method will print timestamps before and after)? – DaveMongoose Jun 18 '14 at 15:40
  • 2
    http://stackoverflow.com/questions/5513558/executing-code-for-every-method-call-in-a-ruby-module – waldyr.ar Jun 18 '14 at 15:43
  • @DaveMongoose Yes, I want only the ones specified. I should have mentioned that – dsp_099 Jun 18 '14 at 15:47
  • @waldyr, that reference is useful, but the question seems slightly different, in that dsp_099 wants the wrapper to apply to only specific methods. – Cary Swoveland Jun 18 '14 at 18:13
  • dsp, earlier I promised to have another solution but have not been able to get to it. I'll work on it this evening, but please do not wait if you'd like to select an answer. – Cary Swoveland Jun 18 '14 at 22:46
  • Thank you. The current answer doesn't quite satisfy my question (but very close!) So I will keep this question open and edit it with my findings in case it helps someone else. Do take a look at how the gem that I found does solve it though - I am not geared to understand it, but perhaps you can. – dsp_099 Jun 18 '14 at 23:02
  • @dsp_099 What else would you need for my solution to satisfy? – Carlos Jun 19 '14 at 09:11
  • @Carlos do_something_to should be able to come first thing after class definition, ideally – dsp_099 Jun 19 '14 at 19:14
  • dsp, I regret that I've not been able to get an approach I have in mind working. My idea was to create the hook `method_added` dynamically, so that it would be executed once for each of `MyClass`' instance methods that are in a list passed to `do_something_to`, right after `MyClass` is built. `method_added` would (dynamically) run `alias_method` and create a new version of the method that performed the pre- and post-operations you mentioned. I'm pretty sure it will work, but I just haven't got it running. – Cary Swoveland Jun 20 '14 at 04:02

1 Answers1

1

I think something like this should work.

The do_something_method will save the original method and then redefine the modified one by printing timestamps before and after calling the original method.

Just one caveat. Make sure you call the do_something_to after the method definition, otherwise it'll not find the original method and will fail.

class Class
  def do_something_to(method)

    class_eval <<-EOS, __FILE__, __LINE__ + 1
      alias_method :modified_#{method}, :#{method}
      def #{method}
        puts Time.now.to_i
        modified_#{method}
        puts Time.now.to_i
      end
    EOS

  end
end

class MyClass

  def test_method
    puts "Hello world"
  end

  do_something_to :test_method
end

MyClass.new.test_method
# => 1403105585
# => "Hello world!"
# => 1403105586
Carlos
  • 2,883
  • 2
  • 18
  • 19
  • Is there a way to do it which avoids said caveat? – dsp_099 Jun 18 '14 at 16:15
  • I can't think of any at this moment, but sounds difficult, because ruby will sequentially execute this code and when calling to do_something_method it needs the method to modify to already exist, much like alias does. – Carlos Jun 18 '14 at 16:20
  • What if the do_something method was added by a Gem? Would that change anything? – dsp_099 Jun 18 '14 at 16:32
  • I don't think so. The problem here is that at the moment you invoke do_something_to the method that you want to modify needs to exist, which also sounds like natural, doesn't it? It's like using a variable, you have to declare it before using it. :) – Carlos Jun 18 '14 at 16:45
  • `alias_method` is not the best way to do this. See the duplicate question linked in the comments of your question. – Max Jun 18 '14 at 17:31