Plough => Ruby

Journey through ruby

OpenStruct to Hash

OpenStruct is a handy class in ruby’s stdlib. I’ve used it in the past for creating test data, instead of using factory girl. It’s fast, and the fact that it behaves like a Hash, makes it super easy for stubbing object behaviours.

The other day, I had this requirement to convert an open struct instance into a hash. I foolishly tried to_hash call, considering that it acts as a hash, so there must be a to_hash call on it. But, unfortunately, there isn’t any to_hash methods on OpenStruct. However, there are other ways to convert an open struct instance to a hash quite easily. Here’s how:

Option 1

os = OpenStruct.new(:id => 12)
os.marshal_dump # returns the hash {:id=>12}

Option 2

os = OpenStruct.new(:id => 12)
os.instance_variable_get(:@table) # returns the hash {:id=>12}

In Ruby2, Inspect Independent of To_s

Ruby 2.0.0 has been released and there are a lot of new things, for example, keyword arguments, TracePoint API, DTrace and so on. Here’s Ruby NEWS file, and some new features in Ruby 2.

One thing to add to that list is that the behaviour of inspect is no longer dependent on to_s. In Ruby 1.9, if to_s was overridden inspect would just execute that code. But in Ruby 2.0, to_s overriding doesn’t affect inspect anymore. Here’s code example for Ruby 1.9.3 and Ruby 2

Installing Any Ruby Source on Your Local Machine With RVM

Update: This approach doesn’t work with the latest version of rvm (1.17.0 stable)

Now, if you are like me you probably have ruby source checked out on your machine to ensure that you stay informed with the newest features and ponder through the ruby source at will. I started wondering what if I want to install the ruby from the source and test my gems out and test it against well-known libraries to see how they fare. Firstly, I thought of doing the ./configure -> make -> make install dance but then I realised…yes you’re absolutely right, why not use RVM.

What RVM? I must be out of mind.

But, hey, RVM downloads ruby source code and installs it on your machine. The only difference is that I have got the ruby source checked-out on my machine and I want to use that as the source. And yes, RVM is awesome. I have spoken to Wayne (via IRC) and the man is an absolute genius, very polite and helpful.

So, somehow, I need to accomplish the following:

  • Hey RVM, don’t download the ruby source.
  • Here’s the location of the ruby source, use that and install it.
  • Voila!

But, how do I do that? I started looking at RVM source code and I am not an expert at bash scripting, so I figured why not just mail RVM mailing list. I got the following helpful response from Micheal Papis.

it's not yet supported to use sources/rubies provided by user, but it
will be in rvm 2.0

as for using it now, you could use $rvm_path/src - and provide your
sources there ... not sure how it will work, you have to experiment a
bit

That clue was enough for me and like he said, I experimented a little bit. When you run something like:

rvm install ruby-1.9.2

RVM downloads the source code into $rvm_path/repos, moves it over to $rvm_path/src and then does the configure, compile dance. I created a symlink in $rvm_path/repos pointing at the local ruby git repository on my machine. And, then I ran the install command. Here’s the output I get:

~/.rvm/src$ rvm install ruby-head
Installing Ruby from source to: /Users/andhapp/.rvm/rubies/ruby-head, this may take a while depending on your cpu(s)...

ruby-head - #fetching 
HEAD is now at 02035ba Merge branch 'trunk' of git://github.com/ruby/ruby into trunk

From github.com:andhapp/ruby
 * branch            trunk      -> FETCH_HEAD
Already up-to-date.
Copying from repo to src path...
ruby-head - #configuring 
ruby-head - #compiling 
ruby-head - #installing 
Retrieving rubygems-1.8.10
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  243k  100  243k    0     0   456k      0 --:--:-- --:--:-- --:--:--  540k

Extracting rubygems-1.8.10 ...
Removing old Rubygems files...
-e:1: Use RbConfig instead of obsolete and deprecated Config.
Installing rubygems-1.8.10 for ruby-head ...
Installation of rubygems completed successfully.
ruby-head - adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
ruby-head - #importing default gemsets (/Users/andhapp/.rvm/gemsets/)
Install of ruby-head - #complete 

This line ‘HEAD is now at 02035ba Merge branch ‘trunk’ of git://github.com/ruby/ruby into trunk’ is the last commit message on my local ruby git repository.

This line ‘From github.com:andhapp/ruby’ reflects the fact that rvm is using my git repository and not ruby’s repository and updating the local source to the latest before installing.

I decided to call the ruby installation ruby-head. You can call it anything you fancy.

Here’s the version on my machine now:

~/.rvm/src$ rvm use ruby-head
Using /Users/andhapp/.rvm/gems/ruby-head
~/.rvm/src$ ruby -v
ruby 2.0.0dev (2011-12-03 trunk 33936) [x86_64-darwin10.8.0]

Awesome!

PS: I am sure there are other details about RVM’s installation procedure that I have missed out and Wayne or Micheal will be kind enough to enlighten me.

Inconsistent Hashes

To clarify, hashes here does not mean the Hash class. Object’s in ruby have a hash method that returns a hash code for an object. The hash is also used in the background by the eql? method. So, it’s safe to say that two objects are equal if their hash methods return the same value.

Now, carry out a simple exercise and see what happens. Fire up an IRB session and run the following code, one by one:

    "test".hash
    1.hash
    [].hash

And note the output after every command run. Now, exit out of IRB and start a fresh session and run the same piece of code again. This time you will see the hash values have changed completely. This might be a bit surprising but it has been implemented in Ruby 1.9 for security reasons as it avoids one from guessing hash values. I picked this up from Ruby-core ML discussion and hope you find it useful.

An Interesting Ruby Method

In this post, I will talk about a ruby method that I had no idea existed which might come handy in debugging your code.

The method I am talking about is set_trace_func. It’s part of the Kernel class and it does what it says. Allows one to set the method tracing on a method. The following snippet explains it further. Here, we have a class, TestingSetProcFunc:

    class TestingSetProcFunc
      def traceThisMethod
        source = 1
        target = 2
      end
    end

To inject set_trace_func in, it needs to be called before the method is invoked, like this:

    
    puts "Event    File:Line                Id          Binding        Classname"
    set_trace_func proc { |event, file, line, id, binding, classname|
      printf "%8s %s:%-2d %20s %12s %8s\n", event, file, line, id, binding, classname
    }

It takes a proc with upto 6 arguments. These are the events with their brief descriptions:

  • c-call (call a C-language routine)
  • c-return (return from a C-language routine)
  • call (call a Ruby method)
  • class (start a class or module definition)
  • end (finish a class or module definition)
  • line (execute code on a new line)
  • raise (raise an exception)
  • return (return from a Ruby method).

Now, to run this we just need to call the method and look at the output:

    tracer = TestingSetProcFunc.new
    tracer.traceThisMethod

This is what the output will look like when you run it on the command line:

    
Event    File:Line                Id          Binding        Classname
c-return test.rb:11       set_trace_func #   Kernel
    line test.rb:13                      #         
  c-call test.rb:13                  new #    Class
  c-call test.rb:13           initialize # BasicObject
c-return test.rb:13           initialize # BasicObject
c-return test.rb:13                  new #    Class
    line test.rb:14                      #         
    call test.rb:2       traceThisMethod # TestingSetProcFunc
    line test.rb:3       traceThisMethod # TestingSetProcFunc
    line test.rb:4       traceThisMethod # TestingSetProcFunc
  return test.rb:5       traceThisMethod # TestingSetProcFunc    

This output gives one a good idea about the work that is happening in the background. The weird looking Binding instance is the context at that particular place in the code. I have just scratched the surface and I am sure there’s a lot one can do with this method. Have a look at the ruby’s test for set_trace_func for more useful ways of employing it.

Ruby Hacking Guide - Chapter 1

I have completed proof-reading and testing the code from old Ruby Hacking Guide and re-releases in the wild again. It still needs some styling work but I will hopefully find time to sort that out this week.

In the meantime, have a read through and see if you spot any mistakes.

Ruby File Append Mode

There are different file modes available to the developer when creating and opening new files in Ruby. One that I didn’t know about was “a” which basically means append to the end of the file rather than starting a new one. What could it be useful for? One example would be as a log file of some sort where one wants to preserve the old data and add new data to the end of the file. Very trivial but helpful if you know about these things. Just makes you a better developer.

A simple code example would be:

    file = File.new("datamapper.log", "a")  

Ruby Hacking Guide

I got hold of an out-of-date Ruby Hacking Guide and it looks very interesting. Therefore, in order to improve my understanding of Ruby internals, I have decided to spend time in reading it and the writing posts about it. The entire book can be downloaded in the form of HTML pages and might not make sense since some of it has been machine translated.

I hope my efforts are useful for anyone willing to venture into Ruby Internals.

Method Default Arguments and Passing Nil Value

This is something very trivial and I kind of observed it whilst programming couple of weeks ago. For example, you had a method like this:

  class SomeClass
    def some_method(some_argument = '')
      raise ArgumentError unless some_argument
    end
  end

Now, I was under the impression that if I passed in a nil into this method, for some reason some_argument will still have the default value(”) as opposed to the nil value passed in but default arguments come into play when no value is passed in for the method arguments. But I was passing in nil which is an instance of NilClass and therefore some_argument gets assigned the nil value and an exception is raised.