• A dozen (or so) ways to start sub-processes in Ruby: Part 1

    Introduction

    It is often useful in Ruby to start a sub-process to run a particular chunk of Ruby code. Perhaps you are trying to run two processes in parallel, and Ruby's green threading doesn't provide sufficient concurrency. Perhaps you are automating a set of scripts. Or perhaps you are trying to isolate some untrusted code while still getting information back from it.

    Whatever the reason, Ruby provides a wealth of facilities for interacting with sub-processes, some better known than others. In this series of articles I will be focusing on running Ruby as a sub-process of Ruby, although many of the techniques I'll be demonstrating are applicable to running any type of program in a sub-process. I'll also be keeping the focus on UNIX-style platforms, such as Linux and Mac OS X. Sub-process handling on Windows differs significantly, and we'll leave that for another series.

    In the first and second articles, I'll demonstrate some of the facilities for starting sub-processes that Ruby possesses out-of-the-box, no requires needed. In the third article we'll look at some tools provided in Ruby's Standard Library which build on the methods introduced in part one. And in the fourth instalment I'll briefly survey a few of the many Rubygems which simplify sub-process interactions.

    Getting Started

    To begin, let's define a few helper methods and constants which we'll refer back to throughout the series. First, let's define a simple method which will serve as our "slave" code - the code we want to execute in a sub-process. Here it is:

    def hello(source, expect_input)
      puts "[child] Hello from #{source}"
      if expect_input
        puts "[child] Standard input contains: \"#{$stdin.readline.chomp}\""
      else
        puts "[child] No stdin, or stdin is same as parent's"
      end
      $stderr.puts "[child] Hello, standard error"
    end
    


    (Note: The full source code for this article can be found at http://gist.github.com/137705)

    This method prints a message to the standard output stream, a message to the standard error stream, and optionally reads and prints a message from the standard input stream. One of the things we'll be exploring in this series is the differing ways in which the various sub-process-starting methods handle standard I/O streams.

    Next, let's define a couple of helpful constants.

    require 'rbconfig'
    THIS_FILE = File.expand_path(__FILE__)
    
    RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
    

    The first, THIS_FILE, is simply the fully-qualified name of the file containing our demo source code. RUBY, the second constant, is set to the fully-qualified path of the running Ruby executable. These constants will come in handy with sub-process methods which require an explicit shell command to be run.

    In order to make the order of events clearer, we'll force the standard output stream into synchronised mode. This will cause it to flush its buffer after every write.

    $stdout.sync = true

    Finally, we'll be surrounding all of the code which follows in the following protective IF-statement:

    if $PROGRAM_NAME == __FILE__
    # ...
    end
    

    This will ensure that the demo code won't be re-executed when we require the source file within sub-processes.

    Method #1: The Backtick Operator

    The simplest way to execute a sub-process in Ruby is with the backtick (`). This method, which harks back to Bourne Shell scripting and Perl, is concise and often gives us exactly as much interaction as we need with a sub-process. The backtick, while it may look like a part of Ruby's core syntax, is technically an operator defined by Kernel. Like most Ruby operators it can be redefined in your own code, although that's beyond the scope of this article. Kernel defines the backtick operator as a method which executes its argument in a subshell.

    puts "1. Backtick operator"
    output = `#{RUBY} -r#{THIS_FILE} -e'hello("backticks", false)'`
    output.split("\n").each do |line|
      puts "[parent] output: #{line}"
    end
    puts
    

    Here, we use backticks to execute a child Ruby process which loads our demo source code and executes the hello method. This yields:

    1. Backtick operator
    [child] Hello, standard error
    [parent] output: [child] Hello from backticks
    [parent] output: [child] No stdin, or stdin is same as parent's
    

    The backtick operator doesn't return until the command has finished. The sub-process inherits its standard input and standard error streams from the parent process. The process' ending status is made available as a Process::Status object in the $? global (aka $CHILD_STATUS if the English library is loaded).

    We can use the %x operator as an alternate syntax for backticks, which enables us to select arbitrary delimiters for the command string. E.g. %x{echo `which cowsay`}.

    Method #2: Kernel#system

    Kernel#system is similar to the backtick operator in operation, with one important difference. Where the backtick operator returns the STDOUT of the finished command, system returns a Boolean value indicating the success or failure of the command. If the command exits with a zero status (indicating success), system will return true. Otherwise it returns false.

    puts "2. Kernel#system"
    success = system(RUBY, "-r", THIS_FILE, "-e", 'hello("system()", false)')
    puts "[parent] success: #{success}"
    puts
    

    This results in:

    2. Kernel#system
    [child] Hello from system()
    [child] No stdin, or stdin is same as parent's
    [child] Hello, standard error
    [parent] success: true
    

    Just like the backtick operator, system doesn't return until its process has exited, and leaves the process exit status in $?. The sub-process inherits the parent process' standard input, output, and error streams.

    As we can see in the example above, when system() is given multiple arguments they are assembled into a single command for execution. This feature can make system() a little more convenient than backticks for executing complex commands. For this reason and because it's more visually apparent in the code, I prefer to use Kernel#system over backticks unless I need to capture the command's output. Note that there are some other ways system() can be called; see the Kernel#exec documentation for the details.

    Method #3: Kernel#fork (aka Process.fork)

    Ruby provides access to the *NIX fork() system call via Kernel#fork. On UNIX-like OSes, fork splits the currently executing Ruby process in two. Both processes run concurrently and independently from that point on. Unlike the methods we've examined so far, fork enables us to execute in-line Ruby code in a sub-process, rather than explicitly starting a new Ruby interpreter and telling it to load our code.

    Traditionally we would need to put in some conditional code to examine the return value of fork and determine whether the code was executing in the parent or child process. Ruby makes it easy to specify what code should be run in the child by allowing us to pass a block to fork. The contents of the block will be run in the child process, after which it will exit. The parent will continue running at the point where the block ends.

    puts "3. Kernel#fork"
    pid = fork do
    hello("fork()", false)
    end
    Process.wait(pid)
    puts "[parent] pid: #{pid}"
    puts
    

    This produces the following output:

    3. Kernel#fork
    [child] Hello from fork()
    [child] No stdin, or stdin is same as parent's
    [child] Hello, standard error
    [parent] pid: 19935
    

    Note the call to Process.wait. Since the process spawned by fork runs concurrently with the parent process, we need to explicitly wait for the child process to finish if we want to synchronize with it. We use the child process ID, returned by fork, as the argument to Process.wait.

    The sub-process inherits its standard error and output streams from the parent. Since fork is a *NIX-only syscall, it will only reliably work on UNIX-style systems.

    Conclusion

    In this first installment in the Ruby Sub-processes series we've looked at three of the simplest ways to start another Ruby process from inside a Ruby program. Stay tuned for part 2, in which we'll delve into some methods for doing more complex communication with spawned sub-processes.

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on June 30th, 2009 by Avdi in Ruby, Tips & Tricks and tagged , .

  • SimpleDB DataMapper Adapter: Progress Report

    From the beginning of Devver, we decided we wanted to work with some new technologies and we wanted to be able to scale easily. After looking at options AWS seemed to have many technologies that could help us build and scale a system like Devver. One of these technologies was SimpleDB. One of the other new things we decided to try was DataMapper (DM) rather than the more familiar ActiveRecord. This eventually let me to work on my own SimpleDB DataMapper adapter.

    Searching for ways to work with SDB using Ruby, we found a SimpleDB DM adapter by Jeremy Boles. It worked well initially but as our needs grew (and to make it compatible with the current version of DM) it became necessary to add and update the features of the adapter. These changes lived hidden in our project's code for awhile, for no other reason than we were too lazy to really commit it all back on GitHub. Recently though there has been a renewed interest about working with on SimpleDB with Ruby. I started pushing the code updates on GitHub, then I got a couple requests and suggestions here and there to improve the adapter. One of these suggestions cam from Ara Howard, who is doing impressive work of his own on Ruby and AWS, specifically SimpleDB. His suggestion on moving from the aws_sdb gem to right_aws, which along with other changes improved performance significantly (1.6x on write, up to 36x on reading large queries over the default limit of 100 objects). Besides performance improvements, we have recently added limit and sorting support to the adapter.

    As I added features, testing the adapter also became slow, (over a minute a run) because the functional tests actually connect to and use SimpleDB. Since Devver is all about speeding up Ruby tests, I decided to get the tests running on Devver. It was actually very easy and sped up the test suite from 1 minute and 8 seconds down to 28 seconds. You can check out how much Devver speeds up the results yourself.

    We are currently using the SimpleDB adapter to power our Devver.net website as well as the Devver backend service. It has been working well for us, but we know that it doesn't cover everyone's needs. Next time you are creating a simple project, give SimpleDB a look, we would love feedback about the DM adapter, and it would be great to get some other people contributing to the project. If anyone does fork my SDB Adapter Github repo, feel free to send me pull requests. Also, let me know if you want to try using Devver as you hack on the adapter, it can really speed up testing, and I would be happy to give out a free account.

    Lastly, at a recent Boulder Ruby users group meet up, the group did a code review for the adapter. It went well and I should finish cleaning up the code and get the improvements suggested by the group committed to GitHub soon.

    Update: The refactorings suggested at the code review are now live on GitHub.

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on June 22nd, 2009 by Dan in Amazon Web Services, Development, Ruby and tagged , , , , .

  • Spellcheck your files with Aspell and Rake

    We recently redid our website. The new site included a new design and much more content explaining what we do. We wanted a quick way to check over everything and make sure we didn't miss any spelling errors or typos. First I started looking for a web service that could scan the site for spelling errors. I found spellr.us, which is nice but would only catch errors once they were live. It also can't scan all of the pages which require being logged in.

    I was pairing with Avdi who thought we should just run Aspell, which worked out great. We were originally trying to just create a simple Emacs macro to go through all our HTML files and check them but in the end created simple Rake tasks, which makes it really easy to integrate spellcheck into CI. After Avdi figured out the commands we needed to use on each file to get the information we needed from Aspell, it was easy to just wrap the command using Rake's FileList. To keep everyone on the same setup, we created a local dictionary of words to ignore or accept and keep that checked into source control as well.

    The final solution grabs all the files you want to spell check, then runs them through Aspell with HTML filtering. We have two tasks: one that runs in interactive mode the the user can fix mistakes and one mode for CI that just fails if it finds any errors.

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on May 26th, 2009 by Dan in Development, Ruby, Tips & Tricks, Tools and tagged , , , .

  • Single-file Sinatra apps with specs baked-in

    It's so easy to create little single-file apps in Sinatra that it almost seems a shame to start a second file just for tests.  The other day Dan and I decided to see if we could create a Sinatra app with everything - including the tests - baked right in.  Here's what we came up with.

    The code switches modes on the name of the executable used to run the file. If we run it with the spec command, we get a test run:

    $ spec -fs sinatra-tests-baked-in.rb

    Example App
    - should serve a greeting
    - should serve content as text/plain

    Finished in 0.007221 seconds

    2 examples, 0 failures

    Otherwise, if we call it as a Ruby program, it runs the Sinatra server as we would expect:

    $ ruby sinatra-tests-baked-in.rb
    == Sinatra/0.9.1.1 has taken the stage on 4567 for development with backup from Thin
    >> Thin web server (v1.0.0 codename That's What She Said)
    >> Maximum connections set to 1024
    >> Listening on 0.0.0.0:4567, CTRL+C to stop

    And there you have it: a true single-file application, specs and all.

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on May 13th, 2009 by Avdi in Development, Hacking, Ruby and tagged , , .

  • Managing Amazon EC2 with your iPhone

    I wanted a quick way when out and about to easily manage our AWS EC2 instances while out and about. It hasn't happened often, but occasionally I am away from the computer and I need to reboot the instances. Perhaps I remember our developer cluster isn't being used and want to shut it down to save some money.

    I didn't find anything simple and free with a quick Google search, so in a about an hour I wrote a nice little Sinatra app that will let me view our instances, shutdown, or reboot any specific instance or all of them. The tiny framework actually turned out to be even more useful as I now have options that let us tail error logs, reboot Apache, reboot mongrel clusters, or execute any common system administration task.

    I won't be going into detail on how to build a iPhone webapp using Sinatra and iUI, because Ben already created an excellent post detailing all of those steps. In fact I used his old project as the template when I created this project. I can't begin to explain how amazingly simple it is to build an iPhone webapp using Sinatra, so if you have been thinking of a quick project I highly recommend it.

    Here are some screen shots showing the final app. (screenshot courtesy of iPhoney):

    ec2 manager home view

    ec2 manager home view.

    ec2 manager describe view

    ec2 manager describe instances view.

    ec2 manager instance view.

    ec2 manager instance view.

    This app uses the Amazon EC2 API Tools to do all the heavy lifting. So this app assumes that you already have the tools installed and working on the machine you want this app to run on. This normally involves installing the tools and setting up some environment variables like EC2_HOME, so make sure you can run ec2-describe-instances from the machine. After that you should just have to change EC2_HOME in the Sinatra app to match the path where you installed the EC2 tools.

    Let me know if you have any issues, it is quick and dirty, but I have already found it useful.

    To run the app:
    cmd> ruby -rubygems ./ec2_manager.rb

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on March 5th, 2009 by Dan in Amazon Web Services, Development, Hacking, Ruby, Tips & Tricks, Tools and tagged , , , , .

  • Using Ruby to Send Update Emails to Our Mentors

    At Devver.net, we send out weekly email updates to an awesome set of mentors. We do this for a number of reasons. First and foremost, we get valuable feedback and advice from our mentors on a variety of issues. But it's also an easy and effective way to keep us on track and even maximize our chances of success. As Paul Graham says in How Not To Die (he was talking directly to YC teams, but you'll get the idea):

    "For us the main indication of impending doom is when we don't hear from you. When we haven't heard from, or about, a startup for a couple months, that's a bad sign.

    ...

    Maybe if you can arrange that we keep hearing from you, you won't die.

    That may not be so naive as it sounds. ... [The] mere constraint of staying in regular contact with us will push you to make things happen, because otherwise you'll be embarrassed to tell us that you haven't done anything new since the last time we talked."

    Foodzie started emailing their mentors early in the summer. We actually borrowed (stole) their email format and best practices.

    One thing we've tried to not do is send out a completely generic email to all our mentors. Depending on the content and the interaction we've had with a specific mentor, we'll adjust his email accordingly. We begin each email with their name and send it directly to them (in other words, we don't put a huge list of addresses in the To, CC, or BCC fields). We do this because we can tailor it and it helps elicit individual responses from each mentor (it's easier to ignore a question if it's sent to a group).

    But, of course, sometimes the emails to a few mentors can be identical. In this case, my not-so-well-kept secret is that I just use a simple Ruby script to send out a duplicate email that appears to be hand-crafted (or at least copied and pasted).

    I've been told that Outlook can perform this functionality easily, but I don't know of any way to do this within Gmail. If there is, let me know so I can feel a little silly (in any case, the Ruby code was fun to write).

    To run this code, you'll need to install the highline gem. You'll also need to add your Gmail account, recipients, subject message, etc. Finally, you'll want to put your message inside a separate file within project directory. That way, you can easily modify, spellcheck, and format to your heart's content before sending.

    You can get the entire gmailr source code (all two files!) at Github. Please use this script for good, not evil - no one likes a spammer. Enjoy!

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on January 20th, 2009 by Ben in Development, Devver, Hacking, Ruby.

  • Ruby people on Twitter

    The Ruby community is always quickly moving, changing, and adopting new things. It is good to keep your ear to the ground so you can learn and adopt things that the community is finding really useful. There are a number of ways to do this, like watching the most popular Ruby projects on GitHub, most active projects on RubyForge, Ruby Reddit, or listening to the Rails podcast. The way I have found most effective is following a good collection of the Ruby community on Twitter, many of the most active Ruby community members and companies are on Twitter. It is where I have first heard of many things going on in Ruby like the recent Merb/Rails merge.

    You can find a great list of 50+ (now 100+) Rubyists to follow on Twitter from RubyLearning. I thought we might as well give out a list of some of the Ruby people Devver.net is following on twitter.

    technoweenie
    jamis / Jamis Buck
    obie / Obie Fernandez
    chadfowler / Chad Fowler
    engineyard / Engine Yard
    d2h / DHH
    rjs / Ryan Singer
    jasonfried / Jason Fried
    37signals
    foodzie
    fiveruns
    _why / why the lucky stiff
    gilesgoatboy / Giles Bowkett
    dlsspy / Dustin Sallings
    julien51 / julien
    rbates / Ryan Bates
    defunkt / Chris Wanstrath
    chrismatthieu / Chris Matthieu
    littleidea / Andrew Clay Shafer
    headius / Charles Nutter
    bascule / Tony Arcieri
    atmos / Corey Donohoe
    ubermajestix / Tyler Montgomery
    raganwald / Reg Braithwaite
    chriseppstein


    of course I have to give a special shout to ourselves:

    danmayer / Dan Mayer
    bbrinck / Ben Brinckerhoff
    devver.net

    If we should be following you also send us an email at contact@devver.net, and we can hook up on Twitter as well.

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on January 8th, 2009 by Dan in Misc, Ruby, Tips & Tricks.

  • Revisiting additional Ruby Tools

    I have heard about new Ruby tools since I did my Ruby Tools Roundup. I am always interested in tools that can help improve our code, so I had to check some of them out. Similar to my last tools post, I will be trying out a tool and writing my general impressions along with the basic usage.

    reek


    I have to start with reek, since it has been the most requested and searched on our site since I originally wrote about tools. reek will help identify code smells, allowing you to fix up your code. Instead of looking at cyclomatic complexity or other metrics, reek looks at patterns to warn you about bad code. Reek currently detects a few code smells (Long Method, Large Class, Feature Envy, Uncommunicative Name, Long Parameter List, Utility Function, Nested Iterators, Control Couple, Duplication) but more are on the way.

    I think this project is useful but would need to be more customized before a nightly run would yield very useful results. The biggest problem I have is the signal to noise ratio seemed pretty high. Reek was warning me about "long methods" that were only 7 statements long, which just isn't something I am concerned about. The warnings on duplicate methods calls can be useful, after running reek on a few files I found a couple places where duplicate method calls were wasting time. Many of the other smells are interesting like 'Feature Envy', and 'Utility Function'. I will need to use reek more before I know if these smells are good indicators or often false positives.

    Below reek finds a utility function next_tick which is definitely a helper function that actually exists in two of our files, which probably should be moved into a helper mixin.

    def next_tick
        if(EM.reactor_running?)
          EM.next_tick do
            yield
          end
        else
          yield
        end
    end

    I am really looking forward to see how the tool progresses. If the project allows for a simple config customization to change the thresholds as well as ignore some files/smells, this could become a very useful tool to help keep a team maintain a high expectation of code quality. It would be useful to get nightly reports about any code that might not meet expectations, so a quick group code review could decide if it is an exception (which can be quickly added to the config) or if the code should be refactored and cleaned up.

    dmayer$ sudo gem install reek
    dmayer$ reek ./lib/client/client.rb
    [Utility Function] Client#next_tick doesn't depend on instance state
    [Long Method] Client#process_done has approx 7 statements
    [Duplication] Client#process_ready calls @buffer.create_reload_msg more than once
    [Long Method] Client#process_ready has approx 10 statements
    [Duplication] Client#report_system_message calls result.msg more than once
    [Feature Envy] Client#report_system_message refers to result more than self
    [Duplication] Client#send_tests calls Time.now more than once
    [Long Method] Client#send_tests has approx 24 statements
    [Feature Envy] Client#send_tests refers to tests more than self
    #check a whole directory
    dmayer$ reek ./lib/client/*

    Towelie


    Towelie helps discover duplication in Ruby code, it will help keep your code DRY. It doesn't have a nice interface at the moment and it is pretty young code. That being said, it can still be a really useful tool to help guide refactoring and code cleanup.

    ~/projects dmayer$ git clone git://github.com/gilesbowkett/towelie.git
    dmayer$ cd ~/projects/devver/
    dmayer$ irb -r ~/projects/towelie/lib/towelie.rb
    irb(main):001:0> @t = Towelie.new
    => #, @model=#>
    irb(main):002:0> @t.parse "lib/client"
    (string):24: warning: useless use of a variable in void context
    => nil
    irb(main):003:0> puts @t.duplicates
    found in:
    lib/client/test_unit_reporter.rb
    lib/client/rspec_reporter.rb
    
    def nl
    report_nl
    end
    
    ... 2 more dupes in the reporters ...
    
    found in:
    lib/client/test_unit_reporter.rb
    lib/client/rspec_reporter.rb
    
    def report(str)
    print(str.to_s)
    end
    
    found in:
    lib/client/sync_client.rb
    lib/client/rev_sync_client.rb
    lib/client/rev_client.rb
    lib/client/client.rb
    
    def quit
    send(@buffer.create_quit_msg)
    end
    
    found in:
    lib/client/sync_client.rb
    lib/client/rev_sync_client.rb
    lib/client/rev_client.rb
    lib/client/client.rb
    
    def send_quit
    send(@buffer.create_quit_msg)
    end
    
    => nil
    irb(main):004:0>

    There are currently many duplications because we are maintaining two clients while deciding what route to eventually take. We have also moved a lot of our shared client code into a mixin, and Towelie finds some methods that really should be moved there as well such as the methods "quit" and "send_quit", which is currently duped in 4 files. Towelie also points to the fact that we should refactor our reporters because they both duplicate code.

    I have always been annoyed with copied and pasted functions accidentally working its way in code, this could be a useful nightly run to keep a team DRY. Sometimes two team members implement the same functionality without even knowing a solution already exists in the code base. If you want to go a bit more in depth, check out Giles Bowkett's (creator of Towelie) How to use Towelie

    Flay


    Flay is another great tool by Ryan Davis who also works on Heckle and Flog which I covered in the past. Flay, like Towelie, helps keep your code DRY, it detects exact and similar code throughout a project. It seems to be more powerful than Towelie, as seen in this Towelie and Flay comparison. My biggest complaint is the current release has some pretty basic output that you see below. The output I got from Towelie was immediately more recognizable and useful, while Flay currently requires you to dig in a bit deeper on your own into its suggestions. An improvement is already being worked on and a verbose output mode should be in the release soon. Once better output is included I think Flay will be immediately useful out of the box even with small amounts of developer effort.

    I like that Flay has weight system, which should make it easy to set some threshold to ignore, high level weights are more likely to be worth your time and attention. One piece of code Flay tagged with a low weight was code that rescued and logged different errors thrown, which while similar actually served a purpose.

    rescue Errno::EISDIR => ed
          @stderr.puts "Error: #{ed.message}" if @stderr
          @stderr.puts "You can't pass a directory to devver only test files. Quitting." if @stderr
          send_quit
        rescue LoadError => le
          @stderr.puts "Error: #{le.message}" if @stderr
          @stderr.puts "Not all of the files can be found. Quitting." if @stderr
          send_quit
        rescue SyntaxError, NameError => se
          @stderr.puts "Error: #{se.message}" if @stderr
          @stderr.puts "This file doesn't appear to be a valid Ruby file. Quitting." if @stderr
          send_quit
    end

    Digging into the Flay results turned up some duplicate code that Towelie had missed. Since Towelie also caught a method that was duped in 4 client files that Flay missed (I was expecting Towelie's results to be a subset of what Flay found), perhaps there is room for both of the tools and learning to work with both a little bit is worth the time. After a little bit of work perhaps one of the projects will become a clearly better option. Until then I will be following both of these projects.

    sudo gem install flay
    dmayer$ flay lib/client/*.rb
    Processing lib/client/client.rb...
    Processing lib/client/mod_client.rb...
    ...
    Processing lib/client/syncer.rb...
    
    Matches found in :defn (mass = 84)
    lib/client/mod_client.rb:86
    lib/client/mod_rev_client.rb:124
    
    Matches found in :block (mass = 57)
    lib/client/client.rb:201
    lib/client/client.rb:205
    lib/client/client.rb:209
    
    ... 6 more results ...
    
    Matches found in :if (mass = 34)
    lib/client/mod_client.rb:63
    lib/client/mod_rev_client.rb:111
    
    Matches found in :defn (mass = 32)
    lib/client/mod_rev_client.rb:36
    lib/client/mod_rev_client.rb:50


    Conclusions


    That should cover it for this Ruby tools post, but I am really enjoying checking out the tools showing up in the Ruby scene. So as always let me know if I missed something, or if there is a tool you would like to see a full write up on. After some of the tools mature a little bit I will have to revisit a few of the tools which are currently in the early stages. I hope the Ruby tools scene keeps as active as it has been lately because there are some interesting projects being worked on.

    honorable mentions (things I didn't think really needed a full write up)


    • metric-fu a great gem to give quick access to a bunch of tools and metrics about your code (RCov, Saikuro, Flog, SCM Churn, and Rails Stats)
    • CruiseControl.rb when you start using all of these tools, continuous integration starts to become more important (or doing nightly runs). CruiseControl.rb is dead simple continuous integration.
    • Simian another code duplication tool, which is mentioned in 3 tools for drying your Ruby code (free for OSS, $99 for a license)
    • Ruby Tidy a tool for cleaning up HTML (I haven't used this in Ruby, but loved the Java version in my Java days)
    • Watir is an open-source library for automating web browsers. It allows you to write tests that are easy to read and maintain. It is simple and flexible.
    • Autotest, if you haven't heard of autotest, check it out, continuously run your tests every time you save a file in your project.
    • Rufus a tool that checks if code you are about to load is safe. Allows you to look for custom patterns that you don't want to run.
    • I wrote about a couple benchmarking tools last time and here is a great article / tutorial on Ruby benchmarking

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on December 3rd, 2008 by Dan in Development, Hacking, Misc, Ruby, Tools.

  • Ruby Beanstalkd distributed worker intermediate lessons

    This post is a follow up to Ruby beanstalkd basics, I will try to make the example code little more interesting and useful. I am calling this is a Ruby beanstalkd intermediate write up, it sets up a few workers and distributes and receives results simultaneously. In this example the code resembles real code a bit more (using a queue cache and block passing). If there is enough interest in the Ruby/beanstalkd community, I will follow up with beanstalkd advanced lessons, and go into how we deal with failure cases such as worker dying during jobs, random jobs failing, processing multiple 'projects' at one time, using job priority settings, and using TTR/timeouts.

    So in this example we are making an estimate of PI. Yes I know that there are far better approximations out there than my simple results, but this was what I came up with for an incredibly simple distributed computing problem. I based my example on the PI Calculation problem from an Introduction to Parallel Computing. The basic idea is that you can calculate pi by guessing random points in a square and then seeing how many points are inside a circle that fits inside the square (PI= 4 * points_in_circle/total_points).

    I made a bunch of comments in the code that should help you follow but there are a few key sections worth pointing out.

    In the Ruby beanstalkd Basics, both the Server and the Clients only used one queue at a time. Now since we are sending on one queue while also listening on another we need access to both queues at once. We simply have a helper function with a queue_cache to make getting and reusing multiple queues incredibly easy.

    def get_queue(queue_name)
        @queue_cache ||= {}
        if @queue_cache.has_key?(queue_name)
          return @queue_cache[queue_name]
        else
          queue = Beanstalk::Pool.new(["#{SERVER_IP}:#{DEFAULT_PORT}"])
          queue.watch(queue_name)
          queue.use(queue_name)
          queue.ignore('default')
          @queue_cache[queue_name] = queue
          return queue
        end
      end

    In the basic example each class had a function that got a job and did some work and deleted the job. It is easy to imagine workers that might have many different kinds of work to do on jobs. In every case they are going to grab a job, work on the job, and delete the job. We decided to break that up and make it easy to just pass a work block when workers get a job.

    def take_msg(queue)
        msg = queue.reserve
        #by calling ybody we get the content of the message and convert it from yml
        body = msg.ybody
        if block_given?
          yield(body)
        end
        msg.delete
      end
    
    #call take_msg like so
    take_msg(queue) do |body|
      #work on body
    end

    One other thing you should keep a look out for in the code below is checking if a queue has any jobs. Many times workers will check if jobs exist and take them, and if there aren't any jobs the process is free to do something else. I do this in this example, the server continually checks incoming results to immediately display. If no results have arrived yet, the server continues sending out job requests as fast as it can. This is useful since taking jobs from beanstalkd is a blocking call. They did add support for non-blocking calls in beanstalkd 1.1, but I haven't started using the newest version yet. I think everything else should be pretty self explanatory, feel free to ask me any questions. To run the code it is the same as before: download beanstalk_intermediate.rb, start beanstalkd, and run the example with ruby.

    $ beanstalkd &
    $ ruby beanstalk_intermediate.rb
    starting distributor
    starting client(s)
    distributor sending out jobs
    .......................................................
    .............................................
    received all the results our estimate for pi is: 3.142776

    # of workers time to complete
    1 real 0m7.282s
    user 0m4.114s
    sys 0m0.978s
    2 real 0m5.667s
    user 0m2.736s
    sys 0m0.670s
    3 real 0m4.999s
    user 0m2.014s
    sys 0m0.515s
    4 real 0m4.612s
    user 0m1.608s
    sys 0m0.442s
    5 real 0m4.517s
    user 0m1.474s
    sys 0m0.416s
    require 'beanstalk-client.rb'
    
    DEFAULT_PORT = 11300
    SERVER_IP = '127.0.0.1'
    #beanstalk will order the queues based on priority, with the same priority
    #it acts FIFO, in a later example we will use the priority
    #(higher numbers are higher priority)
    DEFAULT_PRIORITY = 65536
    #TTR is time for the job to reappear on the queue.
    #Assuming a worker died before completing work and never called job.delete
    #the same job would return back on the queue (in TTR seconds)
    TTR = 3
    
    class BeanBase
    
      #To work with multiple queues you must tell beanstalk which queues
      #you plan on writing to (use), and which queues you will reserve jobs from
      #(watch). In this case we also want to ignore the default queue
      #you need a different queue object for each tube you plan on using or
      #you can switch what the tub is watching and using a bunch, we just keep a few
      #queues open on the tubes we want.
      def get_queue(queue_name)
        @queue_cache ||= {}
        if @queue_cache.has_key?(queue_name)
          return @queue_cache[queue_name]
        else
          queue = Beanstalk::Pool.new(["#{SERVER_IP}:#{DEFAULT_PORT}"])
          queue.watch(queue_name)
          queue.use(queue_name)
          queue.ignore('default')
          @queue_cache[queue_name] = queue
          return queue
        end
      end
    
      #this will take a message off the queue, and process it with the block
      def take_msg(queue)
        msg = queue.reserve
        #by calling ybody we get the content of the message and convert it from yml
        body = msg.ybody
        if block_given?
          yield(body)
        end
        msg.delete
      end
    
      def results_ready?(queue)
        queue.peek_ready!=nil
      end
    
    end
    
    class BeanDistributor < BeanBase
    
      def initialize(chunks,points_per_chunk)
        @chunks = chunks
        @points_per_chunk = points_per_chunk
        @messages_out = 0
        @circle_count = 0
      end
    
      def get_incoming_results(queue)
        if(results_ready?(queue))
          result = nil
          take_msg(queue) do |body|
            result = body.count
          end
          @messages_out -= 1
          print "." #display that we received another result
          @circle_count += result
        else
          #do nothing
        end
      end
    
      def start_distributor
        request_queue = get_queue('requests')
        results_queue = get_queue('results')
        #put all the work on the request queue
        puts "distributor sending out #{@messages} jobs"
        @chunks.times do |num|
          msg = BeanRequest.new(1,@points_per_chunk)
          #Take our ruby object and convert it to yml and put it on the queue
          request_queue.yput(msg,pri=DEFAULT_PRIORITY, delay=0, ttr=TTR)
          @messages_out += 1
          #if there are results get them if not continue sending out work
          get_incoming_results(results_queue)
        end
    
        while @messages_out > 0
          get_incoming_results(results_queue)
        end
        npoints = @chunks * @points_per_chunk
        pi = 4.0*@circle_count/(npoints)
        puts "\nreceived all the results our estimate for pi is: #{pi}"
      end
    
    end
    
    class BeanWorker < BeanBase
    
      def initialize()
      end
    
      def write_result(queue, result)
        msg = BeanResult.new(1,result)
        queue.yput(msg,pri=DEFAULT_PRIORITY, delay=0, ttr=TTR)
      end
    
      def in_circle
        #generate 2 random numbers see if they are in the circle
        range = 1000000.0
        radius = range / 2
        xcord = rand(range) - radius
        ycord = rand(range) - radius
        if( (xcord**2) + (ycord**2) <= (radius**2) )
          return 1
        else
          return 0
        end
      end
    
      def start_worker
        request_queue = get_queue('requests')
        results_queue = get_queue('results')
        #get requests and do the work until the worker is killed
        while(true)
          result = 0
          take_msg(request_queue) do |body|
            chunks = body.count
            chunks.times { result += in_circle}
          end
          write_result(results_queue,result)
        end
    
      end
    
    end
    
    ############
    # These are just simple message classes that we pass using beanstalks
    # to yml and from yml functions.
    ############
    class BeanRequest
      attr_accessor :project_id, :count
      def initialize(project_id, count=0)
        @project_id = project_id
        @count = count
      end
    end
    
    class BeanResult
      attr_accessor :project_id, :count
      def initialize(project_id, count=0)
        @project_id = project_id
        @count = count
      end
    end
    
    #how many different jobs we should do
    chunks = 100
    #how many points to calculate per chunk
    points_per_chunk = 10000
    #how many workers should we have
    #(normally different machines, in our example fork them off)
    workers = 5
    
    # Most of the time you will have two entirely separate classes
    # but to make it easy to run this example we will just fork and start our server
    # and client separately. We will wait for them to complete and check
    # if we received all the messages we expected.
    puts "starting distributor"
    server_pid = fork {
      BeanDistributor.new(chunks,points_per_chunk).start_distributor
    }
    
    puts "starting client(s)"
    client_pids = []
    workers.times do |num|
      client_pid = fork {
        BeanWorker.new.start_worker
      }
      client_pids << client_pid
    end
    
    Process.wait(server_pid)
    #take down the clients
    client_pids.each do |pid|
      Process.kill("HUP",pid)
    end

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on November 19th, 2008 by Dan in Development, Hacking, Ruby.

  • Ruby Tools Roundup

    I collected all of the Ruby tools posts I made this week into a single roundup. You can quickly jump to any tool that interests you or read my reviews start to finish. If you just want to read a individual section here are the previous posts Ruby Code Quality Tools, Ruby Test Quality Tools, and Ruby Performance Tools.

    There have been a bunch of interesting tools released for Ruby lately. I decided to write about a few of my favorite Ruby tools and give some of the new tools a shot as well. Simply put, better tools can help you be a better developer. I am ignoring the entire topic of IDEs as tools, as I have written about Ruby IDEs before, and it is basically a religious war. If you use any Ruby tools I don't mention be sure to let me know as I am always interested in trying something new out.

    Tool Name Description
    Code Quality Tools
    Roodi Roodi gives developers information about common mistakes in their Ruby code. It makes it easy to clean up your code before things start to get ugly.
    Dust Dust is a new tool that will analyze your code, detect unsafe blocks and unused code. Dust is being created by the same mind behind Heckle
    Flog Flog essentially scores an ABC metric, giving you a good understanding of the overall code complexity of any give file or method.
    Saikuro When given Ruby source code, Saikuro will generate a report listing the cyclomatic complexity of each method found.
    Test Quality Tools
    Heckle Heckle helps test your Ruby tests (how cool is that?). Heckle is a mutation tester. It alters/breaks code and verifies that tests fail.
    rcov rcov is the easiest way to get information about your current code coverage.
    Ruby/Rails Performance Tools
    ruby-prof ruby-prof is a fast and easy-to-use Ruby profiler. The first of four tools that can help you solve performance issues.
    New Relic New Relic is one of the three Rails plugin performance debugging and monitoring tools recently released.
    TuneUp TuneUp a Rails performance tool from FiveRuns. This tool has an interesting community built around it as well.
    RubyRun Ruby Run is a Rails performance tool similar to New Relic and TuneUp

    Lets get into it...

    Roodi


    Roodi gives you a bunch of interesting warnings about your Ruby code. We are about to release some code, so I took the opportunity to fix up anything Roodi complained about. It helped identify refactoring opportunities, both with long methods, and overly complex methods. The code and tests became cleaner and more granular after breaking some of the methods down. I even found and fixed one silly performance issue that was easy to see after refactoring, which improved the speed of our code. Spending some time with Roodi looks like it could easily improve the quality and readability of most Ruby projects with very little effort. I didn't solve every problem because in one case I just didn't think the method could be simplified anymore, but the majority of the suggestions were right on. Below is an example session with Roodi


    dmayer$ sudo gem install roodi
    dmayer$ roodi lib/client/syncer.rb
    lib/client/syncer.rb:136 - Block cyclomatic complexity is 5. It should be 4 or less.
    lib/client/syncer.rb:61 - Method name "excluded" has a cyclomatic complexity is 10. It should be 8 or less.
    lib/client/syncer.rb:101 - Method name "should_be_excluded?" has a cyclomatic complexity is 9. It should be 8 or less.
    lib/client/syncer.rb:132 - Method name "find_changed_files" has a cyclomatic complexity is 10. It should be 8 or less.
    lib/client/syncer.rb:68 - Rescue block should not be empty.
    lib/client/syncer.rb:61 - Method name "excluded" has 25 lines. It should have 20 or less.
    lib/client/syncer.rb:132 - Method name "find_changed_files" has 27 lines. It should have 20 or less.
    Found 7 errors.

    After Refactoring:

    ~/projects/gridtest/trunk dmayer$ roodi lib/client/syncer.rb
    lib/client/syncer.rb:148 - Block cyclomatic complexity is 5. It should be 4 or less.
    lib/client/syncer.rb:82 - Rescue block should not be empty.
    Found 2 errors.

    I did have one problem with Roodi - the errors about rescue blocks just seemed to be incorrect. For code like the little example below it kept throwing the error even though I obviously am doing some work in the rescue code.

    Roodi output: lib/client/syncer.rb:68 - Rescue block should not be empty.
    begin
      socket = TCPSocket.new(server_ip,server_port)
      socket.close
      return true
    rescue Errno::ECONNREFUSED
      return false
    end

    Dust


    Dust detects unused code like unused variables,branches, and blocks. I look forward to see how the project progresses. Right now there doesn't seem to be much out there on the web, and the README is pretty bare bones. Once you can pass it some files to scan, I think this will be something really useful. For now I didn't think there wasn't much I could actually do besides check it out. Kevin, who also helped create the very cool Heckle, does claim that code scanning is coming soon, so I look forward to doing a more detailed write up eventually.

    Flog


    Flog gives feedback about the quality of your code by scoring code using the ABC metric. Using Flog to help guide refactoring, code cleanup, and testing efforts can be highly effective. It is a little easier to understand the reports after reading how Flog scores your code, and what is a good Flog score. Once you get used to working with Flog you will likely want to run it often against your whole project after making any significant changes. There are two easy ways to do this a handy Flog Rake task or MetricFu which works with both Flog and Saikuro.

    Running Flog against any subset of a project is easy, here I am running it against our client libraries
    find ./lib/client/ -name \*.rb | xargs flog -n -m > flog.log

    Here some example Flog output when run against our client code.

    Total score = 1364.52395469781
    
    Client#send_tests: (64.3)
        14.3: assignment
        13.9: puts
        10.7: branch
        10.5: send
         4.7: send_quit
         3.4: message
         3.4: now
         2.0: create_queue_test_msg
         1.9: create_run_msg
         1.9: test_files
         1.8: dump
         1.7: each
         1.7: report_start
         1.7: length
         1.7: get_tests
         1.7: -
         1.7: open
         1.7: load_file
         1.6: empty?
         1.6: nil?
         1.6: use_cache
         1.6: exists?
    ModClient#send_file: (32.0)
        12.4: branch
         5.4: +
         4.3: assignment
         3.9: send
         3.1: puts
         2.9: ==
         2.9: exists?
         2.9: directory?
         1.9: strftime
         1.8: to_s
         1.5: read
         1.5: create_file_msg
         1.4: info
    Syncer#sync: (30.8)
        13.2: assignment
         8.6: branch
         3.6: inspect
         3.2: info
         3.0: puts
         2.8: +
         2.6: empty?
         1.7: map
         1.5: now
         1.5: length
         1.4: send_files
         1.3: max
         1.3: >
         1.3: find_changed_files
         1.3: write_sync_time
    Syncer#find_changed_files: (26.2)
        15.6: assignment
         8.7: branch
         3.5: <<
         1.8: to_s
         1.7: get_relative_path
         1.7: >
         1.7: mtime
         1.6: exists?
         1.6: ==
         1.5: prune
         1.4: should_be_excluded?
         1.3: get_removed_files
         1.3: find
    ... and so on ...

    Saikuro


    Saikuro is another code complexity tool. It seems to give a little less information than some of the others. It does generate nice HTML reports. Like other code complexity tools it can be helpful to discover the most complex parts of your projects for refactoring and to help focus your testing. I liked the way Flog broke things down for me into a bit more detail, but either is a useful tool and I am sure it is a matter of preference depending on what you are looking for.

    saikuro screenshot
    Saikuro Screenshot

    Heckle


    Heckle is an interesting tool to do mutation testing of your tests. Heckle currently supports Test:Unit and RSpec, but does have a number of issues. I had to run it on a few different files and methods before I got some useful output that helped me improve my testing. The first problem was it crashing when I passed it entire files (crashing the majority of the time). I then began passing it single methods I was curious about, which still occasionally caused Heckle to get into an infinite loop case. This is a noted problem in Heckle, but -T and providing a timeout should solve that issue. In my case it was actually not an infinite loop timing error, but an error when attempting to rewrite the code, which lead to a continual failure loop that wouldn't time out. When I found a class and method that Heckle could test I got some good results. I found one badly written test case, and one case that was never tested. Lets run through a simple Heckle example.

    #install heckle
    dmayer$ sudo gem install heckle
    
    #example of the infinite loop Error Heckle run
    heckle Syncer should_be_excluded? --tests test/unit/client/syncer_test.rb -v
    Setting timeout at 5 seconds.
    Initial tests pass. Let's rumble.
    
    **********************************************************************
    ***  Syncer#should_be_excluded? loaded with 13 possible mutations
    **********************************************************************
    ...
    2 mutations remaining...
    Replacing Syncer#should_be_excluded? with:
    
    2 mutations remaining...
    Replacing Syncer#should_be_excluded? with:
    ... loops forever ...
    
    #Heckle run against our Client class and the process method
    dmayer$ heckle Client process --tests test/unit/client/client_test.rb
    Initial tests pass. Let's rumble.
    
    **********************************************************************
    ***  Client#process loaded with 9 possible mutations
    **********************************************************************
    
    9 mutations remaining...
    8 mutations remaining...
    7 mutations remaining...
    6 mutations remaining...
    5 mutations remaining...
    4 mutations remaining...
    3 mutations remaining...
    2 mutations remaining...
    1 mutations remaining...
    
    The following mutations didn't cause test failures:
    
    --- original
    +++ mutation
    
     def process(command)
    
       case command
       when @buffer.Ready then
         process_ready
    -  when @buffer.SetID then
    +  when nil then
         process_set_id(command)
       when @buffer.InitProject then
         process_init_project
       when @buffer.Result then
         process_result(command)
       when @buffer.Goodbye then
         kill_event_loop
       when @buffer.Done then
         process_done
       when @buffer.Error then
         process_error(command)
       else
         @log.error("client ignoring invalid command #{command}") if @log
       end
     end
    
    --- original
    +++ mutation
     def process(command)
       case command
       when @buffer.Ready then
         process_ready
       when @buffer.SetID then
         process_set_id(command)
       when @buffer.InitProject then
         process_init_project
       when @buffer.Result then
         process_result(command)
       when @buffer.Goodbye then
         kill_event_loop
       when @buffer.Done then
         process_done
       when @buffer.Error then
         process_error(command)
       else
    -    @log.error("client ignoring invalid command #{command}") if @log
    +    nil if @log
       end
     end
    
    Heckle Results:
    
    Passed    :   0
    Failed    :   1
    Thick Skin:   0
    
    Improve the tests and try again.
    
    #Tests added / changed to improve Heckle results
    
    def test_process_process_loop__random_result
        Client.any_instance.expects(:start_tls).returns(true)
        client = Client.new({})
        client.stubs(:send_data)
        client.log = stub_everything
        client.log.expects(:error).with("client ignoring invalid command this is random")
        client.process("this is random")
      end
    
      def test_process_process_loop__set_id
        Client.any_instance.expects(:start_tls).returns(true)
        client = Client.new({})
        client.stubs(:send_data)
        client.log = stub_everything
        cmd = DataBuffer.new.create_set_ids_msg("4")
        client.expects(:process_set_id).with(cmd)
        client.process(cmd)
      end

    #A final Heckle run, showing successful results
    dmayer$ heckle Client process --tests test/unit/client/client_test.rb
    Initial tests pass. Let's rumble.

    **********************************************************************
    *** Client#process loaded with 9 possible mutations
    **********************************************************************

    9 mutations remaining...
    8 mutations remaining...
    7 mutations remaining...
    6 mutations remaining...
    5 mutations remaining...
    4 mutations remaining...
    3 mutations remaining...
    2 mutations remaining...
    1 mutations remaining...
    No mutants survived. Cool!

    Heckle Results:

    Passed : 1
    Failed : 0
    Thick Skin: 0

    All heckling was thwarted! YAY!!!

    rcov


    rcov is a code coverage tool for Ruby. If you are doing testing you should probably be monitoring your coverage with a code coverage tool. I don't know of a better tool for code coverage than rcov. It is simple to use and generates beautiful, easy-to-read HTML charts showing the current coverage broken down by file. An easy way to make you project more stable is to occasionally spend some time increasing the coverage you have on your project. I have always found it a great way to get back into a project if you have been off of it for awhile. You just need to find some weak coverage points and get to work.
    Rcov Screenshot
    rcov screenshot

    ruby-prof


    ruby-prof does what every other profiler does, but it is much faster than the one built in to Ruby. It also makes it easy to output the information you are seeking to HTML pages, such as call graphs. If you are just looking for a simple write up to get started with ruby-prof I recommend the previous link. I will talk a little more about the kinds of problems I find and how I have solved them with ruby-prof.

    I have used ruby-prof a number of times to isolate the ways to speed up my code. I haven't used it to identify why an entire Rails application is slow (there are better tools I discuss later for that), but if you have a small but highly important piece of code ruby-prof is often the best way to isolate the problem. I used ruby-prof to identified the two slowest lines of code of a spellchecker, which was rewritten to become twice as fast.

    Most recently I used it to identify where the code was spending all of its time in a loop for a file syncer. It turns out that for thousands of files each time through the loop we were continually calling Pathname.new(path).relative_path_from(@dir_path) over and over. Putting a small cache around that call essentially eliminated all delays in our file synchronization. Below is a simple example of how a few lines of code can make all the difference in performance and how easily ruby-prof can help you isolate the problem areas and where to spend your time. I think seeing the code that ruby-prof helped isolate, and the changes made to the code might be useful if you are new to profiling and performance work.

    changes in our spellchecker / recommender

    #OLD Way
     alteration = []
        n.times {|i| LETTERS.each_byte {
            |l| alteration << word[0...i].strip+l.chr+word[i+1..-1].strip } }
     insertion = []
         (n+1).times {|i| LETTERS.each_byte {
            |l| insertion << word[0...i].strip+l.chr+word[i..-1].strip } }
     #NEW Way
        #pre-calculate the word breakups
        word_starts = []
        word_missing_ends = []
        word_ends = []
        (n+1).times do |i|
          word_starts << word[0...i]
          word_missing_ends << word[i+1..-1]
          word_ends << word[i..-1]
        end
    
     alteration = []
        n.times {|i|
          alteration = alteration.concat LETTERS.collect { |l|
            word_starts[i]+l+word_missing_ends[i] } }
     insertion = []
        (n+1).times {|i|
          insertion = insertion.concat LETTERS.collect { |l|
    
            word_starts[i]+l+word_ends[i] } }

    Changes in our file syncer

    #OLD
     path_name = Pathname.new(path).relative_path_from(@dir_path).to_s
     #NEW
     path_name = get_relative_path(path)
    
      def get_relative_path(path)
        return @path_cache[path] if @path_cache.member?(path)
        retval = Pathname.new(path).relative_path_from(@dir_path).to_s
        @path_cache[path] = retval
        return retval
      end

    New Relic


    New Relic is a performance monitoring tool for Rails apps. It has a great development mode that will help you track down performance issues before they even become a problem, and live monitoring so that you can find any hiccups that are slowing down the production application. The entire performance monitoring space for Ruby/Rails seems to be heating up. I guess it is easy to see why, when scaling has been such an issue for some Rails apps. Just playing around with New Relic was exciting and fun. I could quickly track down the slowest pages, and our most problematic SQL calls, in this case I was testing New Relic on Seekler (an old project of ours) since I didn't think I would find much interesting on our current Devver site. Seekler had some glaring performance issues and I think if we had New Relic from the beginning we could have avoided many of them. Sounds like I might have a day project involving New Relic and giving Seekler as much of a performance boost as possible. New Relic turned out to be my favorite of the performance monitoring tools. For a much more detailed writeup check out RailsTips New Relic Review.

    newrelic screenshot
    New Relic screenshot

    TuneUp


    TuneUp another easy-to-install and use Rails performance monitoring solution. The problem I had with TuneUp was I couldn't get it working on test app for these sorts of things. I tried running Seekler with TuneUp, but had no luck. I found that many people on the message boards seemed to be having various compatibility issues. I looked at the TuneUp screencast and the kind of information that they give you and I feel like this would be equal to New Relic if it works for you. I am emailing back and forth with FiveRuns support who have been very attentive and helpful, so if I get it working I will update this section.

    Update: FiveRuns is pretty amazing with their support. I haven't got TuneUp fully working yet, but have made some progress. Some good things to know are that some plugins like safe_erb and output_compression can cause problems with TuneUp. They are aware of the issues, and actively looking into it.

    Ruby Run


    RubyRun provides live performance monitoring and debugging tools. I hadn't ever heard of this product before I started doing some research while writing this blog article. I am sorry to say but this was the hardest to set up, and gave back less valuable information. I think they need a simple screencast on how to get set up and get useful information back. After getting setup and running I could only get ugly CSV reports that didn't tell me much more than the regular Rails log files. I started reading the RubyRun Manual but it was about as long as Moby Dick and all I wanted was how to view simple easy-to-read reports which is a snap in New Relic and TuneUp. Since the site didn't mention RubyRun providing better data than New Relic or TuneUp which were much more user friendly, I don't think I would recommend RubyRun.


    UPDATE: After reading about my difficulties with RubyRun the great folks from Rubysophic got in touch with me. They offered to help me get the tool working and posted a RubyRun quick start guide to their site. I got it working in a snap thanks to an email from their dev and the amazingly simple quick start guide. I still didn't get the same depth of information that I got with New Relic, although RubyRun has a ton of settings so it is likely you can get more depth to the reports. Something worth pointing out is that RubyRun is working on Seekler, which I haven't been able to get TuneUp running on. So if you have been having problems with TuneUp or New Relic, definitely give RubyRun a look. In the end I think the other offerings are slightly more user friendly (less complex settings), and easier to explore the data (link in the feed to both reports, at least when in developer mode). That being said RubyRun offers some great information and options that the others don't and with a bit more UI tuning RubyRun would be at the top of the pack. Thanks to the helpful devs at Rubysophic for helping me to get the most out of RubyRun.

    RubyRun screenshot
    RubyRun screenshot
    RubyRun second screen shot
    screenshot of a different RubyRun report

    That is it, hope you learned about a new Ruby tool. So get to work, try a new tool, and get to know your code a little better than you did before.

    While I was writing this article, people pointed out to me two more tools worth pointing out. I didn't get a chance to try them out or review them, but thought I should point them out. Towlie, helps keep your code dry by finding redundant methods. and finally Source ANalysis (SAN), which is described as, "a Ruby gem for analyzing the contents of source code including comment to script ratios, todo items, declared functions, classes, and much more".

    Got a slow Test::Unit or RSpec suite?
    Devver can run it up to three times faster! Request a beta invite today.

    Posted on October 3rd, 2008 by Dan in Development, Hacking, Ruby, Tips & Tricks.