Paul Barry

How to spy on a Hash in Ruby

February 24, 2010

Let’s say you’re dealing with a large Rails codebase and you’ve got a Hash stored in a global variable or a constant and you want to know who is changing that Hash. Here’s a contrived example:

IMPORTANT_STUFF = {
  :password => "too many secrets"
}

def change_password(h)
  h[:password] = "FAIL"
end

def print_password
  puts IMPORTANT_STUFF[:password]
end

print_password
change_password(IMPORTANT_STUFF)
print_password

Here it’s pretty obvious where the Hash gets changed, but as I said, imagine you are trying to figure this out in a much larger codebase. Something is changing the value of IMPORTANT_STUFF and you don’t know what. So how do you figure out what is? Easy, you do what Lester Freeman would do!

Lester Freeman from The Wire

We set up a sting! We put a wire tap on IMPORTANT_STUFF and monitor all communication with IMPORTANT_STUFF. So how do we do that? Let’s create a class that proxies all communication with a Hash:

class HashSpy

  def initialize(hash={})
    @hash = hash
  end

  def method_missing(method_name, *args, &block)
    puts "***** hash access"
    puts "  before: #{@hash.inspect}"
    r = @hash.send(method_name, *args, &block)
    puts "  after: #{@hash.inspect}"
    puts "  backtrace:\n    #{caller.join("\n    ")}"
    r
  end

end

This uses a couple of interesting Ruby techniques. First, we just pass the actual Hash to the constructor. Then, we use method missing so that any method that is called on the HashSpy will be then called on the Hash and the return value of that method call with be called instead. Note that in Ruby 1.8, this isn’t a transparent proxy because if you called class on the HashSpy, you would get HashSpy, not Hash. In Ruby 1.9, you can have your object inherit from BasicObject, which won’t have those methods, making it easier to be a transparent proxy. In Ruby 1.8, you can use Jim Weirich’s Blank Slate pattern

In HashSpy’s method missing, we use caller to get a backtrace of the current call stack, which will tell us who the perpetrator is.

So, if we just change IMPORTANT_STUFF to be created like this:

IMPORTANT_STUFF = HashSpy.new(
  :password => "too many secrets"
)

Now when we run the program, we’ll get output something like this:

***** hash access
  before: {:password=>"too many secrets"}
  after: {:password=>"too many secrets"}
  backtrace:
    hash_spy.rb:27:in `print_password'
    hash_spy.rb:30
too many secrets
***** hash access
  before: {:password=>"too many secrets"}
  after: {:password=>"FAIL"}
  backtrace:
    hash_spy.rb:23:in `change_password'
    hash_spy.rb:31
***** hash access
  before: {:password=>"FAIL"}
  after: {:password=>"FAIL"}
  backtrace:
    hash_spy.rb:27:in `print_password'
    hash_spy.rb:32
FAIL

And by reading through the output, we can see that the second time the hash is accessed is when the value is changed, so the perpetrator is on line 23 of hash_spy.rb in the change_password method. Here’s the entire script in one gist for reference.

Posted in Technology | Topics Ruby, Rails | 4 Comments

Node.js Presentation

February 8, 2010

Last week I presented on [node.js][node] at the [Baltimore/DC JavaScript Users Group][baltdcjs]. Here’s the [video][video]:

</embed>

Paul Barry on Node.js at February Baltimore JavaScript Meetup from Shea Frederick on Vimeo. To make it easier to follow along, here are the [slides][slides] and the [code][code]. [node]: http://nodejs.org [baltdcjs]: http://www.meetup.com/baltimore-dc-javascript-users/calendar/12299143/ [video]: http://bit.ly/a3RQGj [slides]: http://www.paulbarry.com/node/ [code]: http://github.com/pjb3/node-presentation

Posted in Technology | Topics Javascript, node.js | 2 Comments

Sharing a git repo on the network

February 6, 2010

If you find yourself on a network with other developers you’d like to share a git repo with, here’s a simple way to do that. First, on your machine, the one with the git repo, you run this:

$ git daemon --base-path=/path/to/dir/with/repo --export-all

So if you have a git repo in your home directory called, foo, you would make the base path be your home directory. Then, assuming your IP is 192.168.1.42, others can clone the repo using:

$ git clone git://192.168.1.42/foo
Posted in Technology | Topics Git | 2 Comments

Installing DBSlayer on Mac OS X Snow Leopard

February 2, 2010

DBSlayer is a tool that will wrap your MySQL database with an HTTP REST/JSON API. Here’s how to get it installed on Snow Leopard using Macports. First, make sure you have all the dependencies install via Macports:

$ sudo port install db46
$ sudo port install apr
$ sudo port install apr-util
$ sudo port install mysql5-server

Then, if you try to download the source and install it:

$ cd ~/src
$ wget http://code.nytimes.com/downloads/dbslayer-beta-12.tgz
$ tar xzf
$ cd dbslayer
$ ./configure
$ make
$ make install

You’ll run into this error:

ld: library not found for -ldb-4.6
collect2: ld returned 1 exit status
make[1]: *** [dbslayer] Error 1
make: *** [all-recursive] Error 1

Instead, pass these options to configure:

$ ./configure CFLAGS="-L/opt/local/lib/db46" \
--with-apr-1-config=/opt/local/bin/apr-1-config \
--with-apu-1-config=/opt/local/bin/apu-1-config \
--with-mysql-config=/opt/local/lib/mysql5/bin/mysql_config

Now try to install again:

$ make clean
$ make
$ sudo make install

Next, create the config file, which at it’s most basic, should look something like this:

[my_db]
database=my_db
host=localhost
user=root

Now when you start dbslayer, make sure to give it the full path to the config file:

$ dbslayer -c ~/stuff/my_db.cnf -s my_db

dbslayer starts in the background, so to restart it, you have to find the process and kill it manually. It also doesn’t say whether it worked or not, it just backgrounds with no output. Here’s a little Ruby script to try it out:

require 'open-uri'
require 'rubygems'
require 'json'
require 'pp'

def query_url(sql)
  query_hash = { "SQL" => sql }
  url_args = URI.encode(query_hash.to_json)
  "http://localhost:9090/db?#{url_args}"
end

def exec_query(sql)
  url = query_url(sql)
  open(url) do |f|
    yield JSON.parse(f.read)
  end
end

exec_query "select * from stuff limit 1" do |res|
  pp res
end

Node.js talk at Baltimore/JavaScript Users Group

February 1, 2010

Just a quick heads up that I’ll be talking about Node.js at this Wednesday’s Baltimore/JavaScript Users Group meeting. This will be an introductory talk but will quickly dive into some code examples with Q & A. The talk, just as node.js itself, centers around using Evented, Asynchronous I/O to get better scalability out of network servers, so it should be of interest to anyone doing server-side programming in any language, Java, Perl, Ruby, Python, etc. See you there!

Posted in Technology | Topics Javascript, node.js