Hacker News new | comments | show | ask | jobs | submit login

I prefer to use define_method over method_missing when "metaprogramming". It allows you to explicitly define your "metamethods" up-front.

Using his example of the DoNotDisturb wrapper around InformationDesk, you could easily do something like:

  [:emergency, :flights, :local_transports, :hotels].each do |name|
    define_method(name) do
      check_lunch_break unless name == :emergency
      @desk.send(name)
    end
  end
Now you don't get the side effect of screwing up your exception handling and having to walk on eggshells with the rest of your coding in that class.

The other thing I like about this method is that it gets evaluated once, and then those methods exist. With method_missing, every single time you call one of those methods, it has to go all the way up the chain to find that method until it finally hits method_missing.

EDIT: Oh, and if nothing else, you can at least put the define_method inside the method_missing (I had a project that required method_missing once, so this is what I did). That way, it only has to crawl all the way up the inheritance chain once, then that method gets defined on your class, so subsequent calls to that method don't have to go all the way up the chain again.

Might not be much performance gain in Ruby, but when programming a rails app (where you're likely working on a class that inherits from ActiveRecord and 10 other classes), it helps.




Your approach of defining, even within method_missing is better. Defined methods instead of method_missing is also how Rails 3 got some of it's major speed boosts.


While I know this is a rubyist thread, I want to chime in as a Javascript developer. You can almost do the same thing there - joy! Probably not the best way to do it, but it's 3AM and I'm writing off the top of my head. And it's a little bit messy. And it gets rid of that pesky emergency case.

Within the object constructor:

  // assuming the desk object was inherited and accessible. There are several ways to do this.
  var thisContext = this; // unless you want to do some weird stuff with calling a function that returns a function that calls a function
  ["flights", "local_transports", "hotels"].forEach(function(fn) {
    thisContext[fn] = function() {
      check_lunch_break && desk[fn](arguments);
    };
  });
  
Granted, this doesn't get your methods on the prototype, and will incur a cost every time you create one of these objects. If you properly inherited from the desk class, it will overwrite them. And the emergency function will work just fine, if defined on desk.


Nitpickery indeed, but that won't work as expected. `fn` can be reused within the loop, depending on the runtime, so all methods will end up calling the same `desk[fn]`.


You're right in that it won't work as expected - unless the desk methods are static. In reality, you would probably want to call

  desk[fn].call(thisContext, arguments);


I think you might have misread the code.

Each `fn` is bound exactly once, `forEach` is not a loop.


Touché


That makes a ton of sense. It would also aid significantly in debugging, as you can see every method you created, rather than only being able to infer what they might be.


Oh, and another option would be to pull the common methods out into a module that you can include in each class with one line. I like this option, because then it’s trivial to go create another class called OutForVacation, in which you could include this module, and now you have another wrapper with all the same functionality.


How come no one has mentioned Forwardable and delegators ?




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: