Thursday, May 3, 2007

self centered

So I'm working on this Rails project where I am creating a model object with associations all-at-once using a technique recommended by Mr Josh Susser.

The relevant part of my code looks like this:


def recipient_list=(recipient_emails)
puts "Assigning recipient list: #{recipient_emails}"
if new_record?
@recipient_list = recipient_emails
else
create_recipients(recipient_emails)
end
end

def after_create
self.recipient_list = @recipient_list if @recipient_list
end


Originally this wasn't invoking the recipient_list= method during after_create
because I had removed the "self." thinking it was superfluous. Adding it back in
resulted in the desired behavior. But why?

Best I can figure is that the following looks like a local variable assignment to Ruby:


recipient_list = @recipient_list if @recipient_list


Whereas the following is a method invocation:

self.recipient_list = @recipient_list if @recipient_list

Wednesday, February 21, 2007

Method Calling -- "What's my motivation?"

Sure, you don't really need to know what class a Ruby object belongs to. After all, Ruby is a dynamic language, and instead of relaxing in the knowledge that strict typing means expected methods will always be there, Ruby informs us to relax in the knowledge of duck typing instead.

What I want to know is: what do you do when the expected duck has no bill?

In other words: what do you do when you end up either calling or about to call a method that ain't there?

Seems to me there are two major scenarios:

1) Duck typing -- call the method and hope for the best.

If you call a method that doesn't exist on an object, you end up with a halted program and the message:
NoMethodError: undefined method

So, if you would like to call a method on an object and guard against it not existing at run-time, you can do the following:
begin
   obj.some_unknown_method
rescue NoMethodError
   puts "Glad I used a rescue"
end

2) Probe for the method before calling.

This is very un-Ruby-like as far as I can tell. However, it is possible using the the respond_to? method to see if the desired method does, in fact, exist in the object:
Object.new.respond_to?("respond_to?") => true
Object.new.respond_to?("nonexistent") => false

So, two alternatives that are relatively tight would be:
obj.call_it unless ! obj.respond_to?("call_it")
obj.call_it if obj.respond_to?("call_it")

Thursday, February 15, 2007

Dynamic Method Invocation, Part Uno

I'm sure I'll have a lot to share about dynamic method invocation in Ruby (including why it is that my fingers keep typing "invoice" instead of "invocation"). Hence "part uno."

So in the REST web services I'm working on, Merb automagically creates a method for me based on the name of the resource: resource-name_url. For example, if the name of the resource is "story," then the method name is "story_url." I need to call this method from a common render_create_success method I have created to be used by any successful resource creation. I use the "singularized" name of the controller itself to determine the name of the method to call to get the new resource's location (url). I can get away with this because: 1) I am not writing a general purpose method which will be distributed to all ends of the earth, 2) there is a reliable relationship between a REST resource name and its associated controller.

Anyway, here's the way I call the resource-name_url method:
url_method = self.class.name.downcase.singularize + "_url"
self.send(url_method, item)

... where item is the actual resource / data object (like the story, or anything else that has an id).

I suppose the important piece of information here from a dynamic method invocation standpoint is the usage of the send method including the passing of parameters to the target method.

Monday, February 12, 2007

Spl*t and Parameters

I encountered a splat (*) in the context of a method call:

redirect_to *options

Apparently what this does is to use the contents of the array options as individual parameters in the call to the method redirect_to. So, if this is the signature for the redirect_to method:

def redirect_to(protocol, host, path)

Then array elements will be mapped to the parameters will be mapped as follows:

options[0] -> protocol
options[1] -> host
options[2] -> path

class Test
   def redirect_to(protocol, host, path)
      "#{protocol}://#{host}#{path}"
   end
end

Then, in irb, I can enter the following code:

t = Test.new
t.redirect_to *["https", "www.apple.com", "/macbook"]

... and see the following result:

=> "https://www.apple.com/macbook"

There is also a usage of the splat within the method signature which accumulates all excess parameters into an array:

class Test
   def splat_me (a, *the_rest)
      [a, the_rest]
   end
end

Then, in irb, I can enter the following code:

t = Test.new
t.splat_me 1, 2, 3, 4

... and see the following result:

=> [1, [2, 3, 4]]

Friday, February 9, 2007

Iterate this!

One of the syntactical elements of Ruby that I found slightly baffling was this sort of thing:

["Tuna", "Pawsie", "Bosco"].each {|pet| puts "#{pet}, get off the couch!"}


Intuitively, I could discern what was going on. Mechanically, however, I needed more details to grasp it completely. Once again, while on the elliptical machine at the gym, I came across the section in the Pickaxe describing blocks and iterators. So it turns out that the each method above is a Ruby iterator method. That's a slightly different approach than in other languages where an iterator is an object typically used in a loop which allows you to walk through a set of objects. What's neat (for me, at least) is that you pass in the block of code that you want the iterator method to execute for every object it iterates through. This would be equivalent to a typical iterator while loop in Java where the body of the loop is equivalent to the code block above.

But what actually makes this work, especially when you see multiple parameters specified between the vertical bars? It turns out that the block of code passed into the iterator is executed when the iterator method executes a yield statement. The yield statement can have zero or more parameters which are then sent to the code block. So, in the each iterator method, it walks through each element in the array and passes each one separately to the code block I passed in, resulting in the following output:

Tuna, get off the couch!

Pawsie, get off the couch!

Bosco, get off the couch!


Thursday, February 8, 2007

Who's calling, please?

In a search to discover how I could log the calling of any method, I came across the caller method. This returns an array of strings which is the current call stack.

I did this in irb:

puts caller.join("\n")

Results:

/usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'
/usr/local/lib/ruby/1.8/irb/workspace.rb:52

=> nil


This is the magical "what's the name of the method that called me?" code from here:

def this_method
caller[0] =~ /`([^']*)'/ and $1

end


So you could call this_method from any_method to get the name of the method which would be "any_method" in this case. Follow?

Oh, and I haven't yet figured out how to log a method call the way I want to. In merb you can use a before filter, but your method doesn't call the filter method -- merb does -- so you don't get the method name you want returned using caller.

Updated 13-Feb-2007:

Found the following related method in the merb code:

def current_method_name(depth=0)
   caller[depth] =~ /`(.*)'$/; $1
end

Wednesday, February 7, 2007

Mission: Learn Stuff About Ruby

Every time I am on the elliptical machine at the gym and bring the Ruby book with me, I learn something new. Today it was:

Assignment in parallel, e. g. a, b = 1, 2

So it occurred to me that I should learn one new thing about Ruby every (work) day for the next 4 weeks. I'll keep you posted on what I learn.

Update 15-Feb-2007:

OK, so "every day" was a little ambitious. So sue me.