Ruby Tricks

Posted on August 30, 2006

How often did you write this kind of code?

    def initialize(arg1, arg2, arg3)
      @arg1 = arg1
      @arg2 = arg2
      @arg3 = arg3
    end

All this constructor does it propagates parameters to initialize method to instance variables. In Ruby, there is a better way:

    class Binding
      def local_to_instance
        eval("local_variables").each do |name|
          eval("self").instance_variable_set("@#{name}", eval(name))
        end
      end

      alias :kernel_eval :eval
      def eval(code)
        kernel_eval(code, self)
      end
    end

    class Test
      attr_accessor :arg1, :arg2, :arg3

      def initialize(arg1, arg2, arg3)
        binding.local_to_instance
      end
    end

    t = Test.new(1,2,3)
    puts t.arg1
    # => 1
    puts t.arg3
    # => 3
    p t
    # => #<Test:0x1c6600 @arg1=1, @arg3=3, @arg2=2>

Note that Ruby 1.9 already provides Binding#eval method, so you don't need this kernel_eval hack.

I know this kind of silly and, I guess, inefficient. But still.

Breakpoint extension breakage

Posted on August 25, 2006

Many Rails users enjoyed this little gem developed by Florian Groß. But as Mauricio Fernandez noted in his blog, this extension is broken with the brand new Ruby 1.8.5. In order to make its magic possible, breakpoint library relied on the implementation of Binding.of_caller, which in turn relied on the bug in Ruby's implementation of trace calls. With new release, this bug has been patched.

Mauricio has started working of fixing this problem and proposed a new method binding_n(n), which would return a binding for any frame in the call stack. But you don't have to wait for his work to complete. ruby-debug has this functionality already implemented for you.

Consider this small snippet:

    require "rubygems"
    require 'ruby-debug'

    module Kernel
      def binding_n(n = 0)
        frame = Debugger.current_context.frames[n+1]
        raise "Unknown frame #{n}" unless frame
        frame.binding 
      end
    end

    def test
      puts eval("var", binding_n(1))
    end

    Debugger.start do
      var = 'Hello'
      test
    end

And most likely, I will add this method to the next version of ruby-debug. It can be handy sometimes.

What is Rinda anyway?

Posted on August 24, 2006

Rinda framework is part of the standard Ruby library. But what is it and how can it be useful? This question I will try to address in this article.

In the center of Rinda is a concept called tuple spaces. The idea of tuple spaces was initially introduced in the programming language Linda in 1982 which was designed to ease the implementation of distributed applications. With Rinda your application consists of many simultaneously running programs located anywhere on your network. All these programs are coordinated by a single process, tuple space. In essence, tuple space is a shared repository of objects accessed via network. What is interesting is that TupleSpace itself doesn't coordinate programs, but rather programs use TupleSpace to coordinate their workflow with each other. TupleSpace service provides a handful set of operations that I am going to describe next. What is really interesting is that just by using this small number of methods you can implement very powerful concepts.

What kind of objects TupleSpace can hold?

In essence, Rinda tuple space can hold Arrays or Hashs. Actually, it can hold any object that resembles (via ducktyping):

  • array, by implementing size and [] methods.
  • hash, by implementing each {|k,v| ...} and keys methods.

Asynchronous remote calls

Posted on August 16, 2006

I remember a long time ago when I was programming C++ and using CORBA/COM(+) most of the time, frameworks spent reams of code dedicated to make one simple thing possible: asynchronous remote calls. You start a remote call, then go about your business and return for a result.

I remember I spent huge amount of time trying to make this thing work. Let's see how Ruby can handle this task. I'm going to use XMLRPC framework for demonstration purposes. Let's say you want to fetch the latest post from your Typo blog and you want to do it asynchronously of course.

    require "xmlrpc/client"

    client = XMLRPC::Client.new2("http://www.yourblog.com/backend/xmlrpc")

    post = Thread.start {
      client.call_async(*%w|metaWeblog.getRecentPosts blog user pwd 1|).first
    }

    # do your stuff here
    $stdout.sync = true
    5.times do print '.'; sleep 1; end
    puts
    # done

    puts post.value['url']

In this code I rely on the fact that Thread#value method returns a result of the last statement of the thread's block and it joins on the thread if it's still running.

Not too bad for one-liner!

ruby-debug 0.3

Posted on August 15, 2006

OK, here's a new version of ruby-debug. Most changes are related to the remote debugging and improvements targeted for a possible GUI integration:

  • Wait for a client connection. With this option on, the debugger will wait for a connection, before returning control to the script. You can enable it with -w option of rdebug script or by calling Debugger.wait_connection = true.

  • Stop on connect. With this option on, the debugger will stop when a remote client establishes a connection. rdebug script has this parameter enabled by default, but you can disable it with -n option. In your own script you can use Debugger.stop_on_connect = true to activate it.

  • Controlling thread. When activating a remote debugger, a control thread is activated as well. It listens on the socket (port 8990, by default) and accepts a number of commands that you can use to control a debugger, such as add/delete breakpoints, interrupt an application, etc. This is quite helpful for a GUI frontend, where you can set breakpoints at any time while your application is running.

There are also a couple of additions to the debugger commands:

  • l[ist] = - displays the current exception point.

  • f[rame] n - switches to nth frame. You can see the list of frames and their numbers with frame or where command.

  • tm[ate] - if you are running Mac OSX and addicted to TextMate, this command opens the current file using this editor.

That's all for now. Enjoy.