Showing posts with label method_missing. Show all posts
Showing posts with label method_missing. Show all posts

Tuesday, March 10, 2009

Duck Typing

I learnt how to program in Ruby, and as we all know it's a very flexible language. Recently I've been doing a lot of stuff in C# on windows, and a lot more stuff in Objective-C on the iPhone. Something finally clicked for me, when I was using these languages, and that was Duck Typing.

Ruby, unlike the C style languages, couldn't care less about what type of object it is dealing with. Now before you start, I will repeat, it couldn't care less about what type of object it is dealing with.

What Ruby cares about is if object responds to the method you're sending it. In Ruby there is even a respond_to? method to assert this. i.e.

1.respond_to?(:to_s) #=> true
1.respond_to?(:foo_bar) #=> false

So you can have two objects, of totally differing types, and instead of asserting type like you might in say C#, you can instead check that the object responds to a method. The great thing about this, is that if it doesn't and you want it to, you can use Ruby's meta programming to dynamically inject that method into that object. For instance

unless 1.respond_to?(:foo_bar)<br />   1.class.class_eval do<br />      def foo_bar<br />         self * 100<br />      end<br />   end<br />end

1.foo_bar #=> 100
2.foo_bar #=> 200
30.foo_bar #=> 30000

12.2.foo_bar
#=> NoMethodError: undefined method `foo_bar' for 12.2:Float

So we've used class_eval to dynamically inject the method foo_bar in the class of 1(Fixnum), and so now all instances of Fixnum will respond to that method. We've never even checked it's type, we don't care, all we know is that we want it to respond to the method foo_bar and now it does. Obviously our Float class doesn't have this method hence the reason that 12.2 has raised the undefined method error. Any guesses on how to make this work :D, yeah that's right, class_eval and respond_to? will do it, just like we did with Fixnum.

So in summary what is the point of Duck Typing? Why not just check the class, and respond with what we as coders know it can and cannot do(or think we know it can do)?

Well, seriously why do you care what class it is? When what you really wanted to know is, whether it's going to respond to a method? Sometimes class and type is important, and obviously those features are there and should be used when class is important, but sometimes you just need to stop and ask yourself.

Do I can about type or do I just want to know if I can execute a method on this object?


Tuesday, March 4, 2008

Ruby's Method Missing

I love programming in Ruby. Sometimes I need to do stuff in other languages, and for all intensive purposes, it's fine. I've no problem firing up Visual Studio and hacking out a bit of C#. But curly braces and brackets around every function aside and I think Ruby offers something that is often overlooked in lots of other languages. Smalltalk introduced the idea some time ago with doesNotUnderstand, but I'll explain it in the context of Ruby.

In Ruby everything is an object:

"A String".class => String

1.class => Fixnum.

Let me repeat everything is an object
1.class.class => Class

And from the wonderful Programming Ruby book by Dave Thomas in reference to Ruby's class Object:
"Object is the parent class of all classes in Ruby . Its methods are therefore available to all objects unless explicitly overridden." -- GetItHere

One of the Object methods(functions are called methods in Ruby) which are automatically inferred to all objects is method_missing. Method missing is where your method request will end up if the method you called cannot be found.

Here is a really simple trivial example:
-----------------------------------------------------
class Fixnum
def method_missing(method, arg)
puts "Called: #{method}"
return self * arg
end
end
puts 10.mutiply_by(100)
----------------------------------------------------

--------------------------
OUTPUT:
Called: multiply_by
1000
-------------------------
Let's just explain this quickly. It's trivial and you'd never bother doing this but it does explain the concept. We're essentially re-writing the method_missing method for the Fixnum class. This will not effect the method_missing class in any other class, including most importantly object.
You can prove this to yourself by changing the 10 to a 10.2, you'll now throw an error explain that multiply_by is not a method of the Float class, since 10.2 is a float not a Fixnum.
Anyhow we digress, back to the code. method_missing takes at least 2 args, the first one is always the method that was attempting to be called, in the case of our Fixnum method_missing it's called method, and I'm also only assuming one argument from the caller. But is could easily take an array of args even a proc object.
So we write to stdout using puts to log what method was being called, and then simply take the args and multiply it by self, in this scope that is 10. Hence we get 1000 since 10*100 == 1000

But of course this is stupid, you'd never do this. The real power of method_missing comes in when you want to dynamically deal with methods. Creating API's for instance. You can now dynamically catch(rescue is Ruby land) anything that isn't a method. You can log it and return a proper message. All in 5 lines of code.

---------------------------------------------
class User
....accessors and methods
private
def method_missing(method, *args)
puts "Unavailable method called on #{self.class}: #{method}
puts "With args: #{args.inspect}"
return "Method: #{method} does not exist"
end
end
---------------------------------------------

This will allow methods called on the user object that do not exist to simply return you a text string, rather than raising an error.
But I want to raise an error you say, I want to log it and then raise the error. Fine, no problems, change the return line to:
super(method, args)

You'll now call the original method_missing from the object class after you've executed puts. In essence you get to do stuff before your error is raised. Cool ehh.

You'll see method_missing splattered all over Ruby code, and there are some outstanding uses of it out there.

Here's an example from Rails:
Check out routing.rb in Rails core for example. method_missing is used to generate all those named routes.
So in your routes table when you name a route like so:
map.manage_user "some/nicely_formated_url", :controller => "blah", :action => "blah"
What actually happens is method_missing grabs that, and calls a method add_named_route, which in turn adds your route with the custom name to the hash called named_routes and also to an array called routes which is later used to determine where URLs are to be routed to.... Wicked!