• Making Rack::Reloader work with Sinatra

    by Ben

    According to the Sinatra FAQ, source reloading was taken out of Sinatra in version 0.9.2 due to "excess complexity" (in my opinion, that's a great idea, because it's not a feature that needs to be in minimal a web framework like Sinatra). Also, according to the FAQ, Rack::Reloader (included in Rack) can be added to a Sinatra application to do source reloading, so I decided to try it out.

    Setting up Rack::Reloader is easy:

    require 'sinatra'
    require 'rack'
    
    configure :development do
      use Rack::Reloader
    end
    
    get "/hello" do
      "hi!"
    end
    
    $ ruby hello.rb
    == Sinatra/0.9.4 has taken the stage on 4567 for development with backup from Thin
    >> Thin web server (v1.2.4 codename Flaming Astroboy)
    >> Maximum connections set to 1024
    >> Listening on 0.0.0.0:4567, CTRL+C to stop
    [on another terminal]
    $ curl http://localhost:4567/hello
    hi!
    

    If you add another route, you can access it without restarting Sinatra:

    get "/goodbye" do
      "bye!"
    end
    
    $ curl http://localhost:4567/goodbye
    bye!
    

    But what happens when you change the contents of a route?

    get "/hello" do
      "greetings!"
    end
    
    $ curl http://localhost:4567/hello
    hi!
    

    You still get the old value! What is going on here?

    Rack::Reloader simply looks at all files that have been required and, if they have changed on disk, re-requires them. So each Sinatra route is re-evaluated when a reload happens.

    However, identical Sinatra routes do NOT override each other. Rather, the first route that is evaluated is used (more precisely, all routes appended to a list and the first matching one is used, so additional identical routes are never run).

    We can see this with a simple example:

    require 'sinatra'
    
    get "/foo" do
     "foo"
    end
    
    get "/foo" do
     "bar"
    end
    
    $ curl http://localhost:4567/foo
    foo   # The result is 'foo', not 'bar'
    

    Clearly, Rack::Reloader is not very useful if you can't change the contents of any route. The solution is to throw away the old routes when the file is reloaded using Sinatra::Application.reset!, like so:

    configure :development do
      Sinatra::Application.reset!
      use Rack::Reloader
    end
    
    $ curl http://localhost:4567/hello
    greetings!
    

    Success!

    A word of caution: you MUST call reset! very early in your file - before you add any middleware, do any other configuration, or add any routes.

    This method has worked well enough for our Sinatra application. However, code reloading is always tricky and is bound to occasionally produce some weird results. If you want to significantly reduce the chances for strange bugs (at the expense of code loading time), try Shotgun or Rerun. Happy reloading!

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on December 21st, 2009 by Ben in Hacking, Tips & Tricks, Tools and tagged , , .

  • Improving Code using Metric_fu

    by Dan

    Often, when people see code metrics they think, "that is interesting, I don't know what to do with it." I think metrics are great, but when you can really use them to improve your project's code, that makes them even more valuable. metric_fu provides a bunch of great metric information, which can be very useful. But if you don't know what parts of it are actionable it's merely interesting instead of useful.

    One thing when looking at code metrics to keep in mind is that a single metric may not be as interesting. If you look at a metric trends over time it might help give you more meaningful information. Showing this trending information is one of our goals with Caliper. Metrics can be your friend watching over the project and like having a second set of eyes on how the code is progressing, alerting you to problem areas before they get out of control. Working with code over time, it can be hard to keep everything in your head (I know I can't). As the size of the code base increases it can be difficult to keep track of all the places where duplication or complexity is building up in the code. Addressing the problem areas as they are revealed by code metrics can keep them from getting out of hand, making future additions to the code easier.

    I want to show how metrics can drive changes and improve the code base by working on a real project. I figured there was no better place to look than pointing metric_fu at our own devver.net website source and fixing up some of the most notable problem areas. We have had our backend code under metric_fu for awhile, but hadn't been following the metrics on our Merb code. This, along with some spiked features that ended up turning into Caliper, led to some areas getting a little out of control.

    Flay Score before cleanup

    When going through metric_fu the first thing I wanted to start to work on was making the code a bit more DRY. The team and I were starting to notice a bit more duplication in the code than we liked. I brought up the Flay results for code duplication and found that four databases models shared some of the same methods.

    Flay highlighted the duplication. Since we are planning on making some changes to how we handle timestamps soon, it seemed like a good place to start cleaning up. Below are the methods that existed in all four models. A third method 'update_time' existed in two of the four models.

     def self.pad_num(number, max_digits = 15)
        "%%0%di" % max_digits % number.to_i
      end
    
      def get_time
          Time.at(self.time.to_i)
      end
    

    Nearly all of our DB tables store time in a way that can be sorted with SimpleDB queries. We wanted to change our time to be stored as UTC in the ISO 8601 format. Before changing to the ISO format, it was easy to pull these methods into a helper module and include it in all the database models.

    module TimeHelper
    
      module ClassMethods
        def pad_num(number, max_digits = 15)
          "%%0%di" % max_digits % number.to_i
        end
      end
    
      def get_time
          Time.at(self.time.to_i)
      end
    
      def update_time
        self.time = self.class.pad_num(Time.now.to_i)
      end
    
    end
    

    Besides reducing the duplication across the DB models, it also made it much easier to include another time method update_time, which was in two of the DB models. This consolidated all the DB time logic into one file, so changing the time format to UTC ISO 8601 will be a snap. While this is a trivial example of a obvious refactoring it is easy to see how helper methods can often end up duplicated across classes. Flay can come in really handy at pointing out duplication that over time that can occur.

    Flog gives a score showing how complex the measured code is. The higher the score the greater the complexity. The more complex code is the harder it is to read and it likely contains higher defect density. After removing some duplication from the DB models I found our worst database model based on Flog scores was our MetricsData model. It included an incredibly bad high flog score of 149 for a single method.

    File Total score Methods Average score Highest score
    /lib/sdb/metrics_data.rb 327 12 27 149

    The method in question was extract_data_from_yaml, and after a little refactoring it was easy to make extract_data_from_yaml drop from a score of 149 to a series of smaller methods with the largest score being extract_flog_data! (33.6). The method was doing too much work and was frequently being changed. The method was extracting the data from 6 different metric tools and creating summary of the data.

    The method went from a sprawling 42 lines of code to a cleaner and smaller method of 10 lines and a collection of helper methods that look something like the below code:

      def self.extract_data_from_yaml(yml_metrics_data)
        metrics_data = Hash.new {|hash, key| hash[key] = {}}
        extract_flog_data!(metrics_data, yml_metrics_data)
        extract_flay_data!(metrics_data, yml_metrics_data)
        extract_reek_data!(metrics_data, yml_metrics_data)
        extract_roodi_data!(metrics_data, yml_metrics_data)
        extract_saikuro_data!(metrics_data, yml_metrics_data)
        extract_churn_data!(metrics_data, yml_metrics_data)
        metrics_data
      end
    
      def self.extract_flog_data!(metrics_data, yml_metrics_data)
        metrics_data[:flog][:description] = 'measures code complexity'
        metrics_data[:flog]["average method score"] = Devver::Maybe(yml_metrics_data)[:flog][:average].value(N_A)
        metrics_data[:flog]["total score"]   = Devver::Maybe(yml_metrics_data)[:flog][:total].value(N_A)
        metrics_data[:flog]["worst file"] = Devver::Maybe(yml_metrics_data)[:flog][:pages].first[:path].fmap {|x| Pathname.new(x)}.value(N_A)
      end
    

    Churn gives you an idea of files that might be in need of a refactoring. Often if a file is changing a lot it means that the code is doing too much, and would be more stable and reliable if broken up into smaller components. Looking through our churn results, it looks like we might need another layout to accommodate some of the different styles on the site. Another thing that jumps out is that both the TestStats and Caliper controller have fairly high churn. The Caliper controller has been growing fairly large as it has been doing double duty for user facing features and admin features, which should be split up. TestStats is admin controller code that also has been growing in size and should be split up into more isolated cases.

    churn results

    Churn gave me an idea of where might be worth focusing my effort. Diving in to the other metrics made it clear that the Caliper controller needed some attention.

    The Flog, Reek, and Roodi Scores for Caliper Controller:

    File Total score Methods Average score Highest score
    /app/controllers/caliper.rb 214 14 15 42

    reek before cleanup

    Roodi Report
    app/controllers/caliper.rb:34 - Method name "index" has a cyclomatic complexity is 14.  It should be 8 or less.
    app/controllers/caliper.rb:38 - Rescue block should not be empty.
    app/controllers/caliper.rb:51 - Rescue block should not be empty.
    app/controllers/caliper.rb:77 - Rescue block should not be empty.
    app/controllers/caliper.rb:113 - Rescue block should not be empty.
    app/controllers/caliper.rb:149 - Rescue block should not be empty.
    app/controllers/caliper.rb:34 - Method name "index" has 36 lines.  It should have 20 or less.
    
    Found 7 errors.
    

    Roodi and Reek both tell you about design and readability problems in your code. The screenshot of our Reek 'code smells' in the Caliper controller should show how it had gotten out of hand. The code smells filled an entire browser page! Roodi similarly had many complaints about the Caliper controller. Flog was also showing the file was getting a bit more complex than it should be. After picking off some of the worst Roodi and Reek complaints and splitting up methods with high Flog scores, the code had become easily readable and understandable at a glance. In fact I nearly cut the Reek complaints in half for the controller.

    Reek after cleanup

    Refactoring one controller, which had been quickly hacked together and growing out of control, brought it from a dizzying 203 LOC to 138 LOC. The metrics drove me to refactor long methods (52 LOC => 3 methods the largest being 23 LOC), rename unclear variable names (s => stat, p => project), move some helpers methods out of the controller into the helper class where they belong. Yes, all these refactorings and good code designs can be done without metrics, but it can be easy to overlook bad code smells when they start small, metrics can give you an early warning that a section of code is becoming unmanageable and likely prone to higher defect rates. The smaller file was a huge improvement in terms of cyclomatic complexity, LOC, code duplication, and more importantly, readability.

    Obviously I think code metrics are cool, and that your projects can be improved by paying attention to them as part of the development lifecycle. I wrote about metric_fu so that anyone can try these metrics out on their projects. I think metric_fu is awesome, and my interest in Ruby tools is part of what drove us to build Caliper, which is really the easiest way try out metrics for your project. Currently, you can think of it as hosted metric_fu, but we are hoping to go even further and make the metrics clearly actionable to users.

    In the end, yep, this is a bit of a plug for a product I helped build, but it is really because I think code metrics can be a great tool to help anyone with their development. So submit your repo in and give Caliper hosted Ruby metrics a shot. We are trying to make metrics more actionable and useful for all Ruby developers out, so we would love to here from you with any ideas about how to improve Caliper, please contact us.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on October 27th, 2009 by Dan in Development, Devver, Hacking, Misc, Ruby, Testing, Tools and tagged , , , , , .

  • Beware of pipe duplication in subprocesses

    by Avdi

    I've been writing a lot lately about managing subprocesses in Ruby, and in that vein I thought I'd write up an interesting subprocess gotcha we encountered recently.

    I won't go into the details of how we discovered the issue, except to say that it manifested when using the Main and Servolux gems (both of which I highly recommend) together to start daemon processes. The problem can be boiled down to this brief snippet, distilled by Tim Pease:

    ruby -e 'IO.popen("ruby -e \"STDOUT.dup; STDOUT.close; sleep\"").read
    

    If you try to execute that line, the #read will hang indefinitely, even though STDOUT was closed in the child process. Why? Tim explains:

    "...the parent script will block waiting for pipe to close in the child process. However, because there is a duplicate of the STDOUT file descriptor in the child, both must be closed or the parent will never unblock from the read. [...] ALL file descriptors have to be closed before an EOF is sent through the pipe to the parent. Hence, the read will never return."

    The moral of this story: don't try to read from a subprocess which duplicates STDOUT or STDERR handles. Or if you do, take precautions by using calls which don't wait for an EOF - such as #readpartial - instead of #read.

    I can take no credit for solving this one. All the honor goes to Ara T. Howard, who expended much mental energy and beer to get to the bottom of the problem; and Tim Pease, who neatly summarised the issue and explained it in terms that even I could understand. I'm documenting it here in case anyone else runs into it.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on October 22nd, 2009 by Avdi in Uncategorized and tagged , , , , , , .

  • A Dozen (or so) Ways to Start Subprocesses in Ruby: Part 3

    by Avdi

    In part 1 and part 2 of this series, we took a look at some of Ruby's built-in ways to start subprocesses. In this article we'll branch out a bit, and examine some of the tools available to us in Ruby's Standard Library. In the process, we'll demonstrate some lesser-known libraries.

    Helpers

    First, though, let's recap some of our boilerplate code. Here's the preamble code which is common to all of the demonstrations in this article:

    require 'rbconfig'
    
    $stdout.sync = true
    
    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"
      puts "[child] DONE"
    end
    
    THIS_FILE = File.expand_path(__FILE__)
    
    RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
    

    #hello is the method which we will be calling in a Ruby subprocess. It reads some text from STDIN and writes to both STDOUT and STDERR.

    THIS_FILE and RUBY contain full paths for the demo source file and the the Ruby interpreter, respectively.

    Method #6: Open3

    The Open3 library defines a single method, Open3#popen3(). #popen3() behaves similarly to the Kernel#popen() method we encountered in part 2. If you remember from that article, one drawback to the #popen() method was that it did not give us a way to capture the child process' STDERR stream. Open3#popen3() addresses this deficiency.

    Open3#popen3() is used very similarly to Kernel#popen() (or Kernel#open() with a '|' argument). The difference is that in addition to STDIN and STDOUT handles, popen3() yields a STDERR handle as well.

    puts "6. Open3"
    require 'open3'
    include Open3
    popen3(RUBY, '-r', THIS_FILE, '-e', 'hello("Open3", true)') do
      |stdin, stdout, stderr|
      stdin.write("hello from parent")
      stdin.close_write
      stdout.read.split("\n").each do |line|
        puts "[parent] stdout: #{line}"
      end
      stderr.read.split("\n").each do |line|
        puts "[parent] stderr: #{line}"
      end
    end
    puts "---"
    

    When we execute this code, the result shows that we have captured the subprocess' STDERR output:

    6. Open3
    [parent] stdout: [child] Hello from Open3
    [parent] stdout: [child] Standard input contains: "hello from parent"
    [parent] stdout: [child] DONE
    [parent] stderr: [child] Hello, standard error
    ---
    

    Method #7: PTY

    All of the methods we have considered up to this point have shared a common limitation: they are not very well-suited to interfacing with highly interactive subprocesses. They work well for "filter"-style commands, which read some input, produce some output, and then exit. But when used with interactive subprocesses which wait for input, produce some output, and then wait for more input (etc.), their use can result in deadlocks. In a typical deadlock scenario, the expected output is never produced because input is still stuck in the input buffer, and the program hangs forever as a result. This is why, in previous examples, we have been careful to call #close_write on subprocess input handles before reading any output.

    Ruby ships with a little-known and poorly-documented standard library called "pty". The pty library is an interface to BSD pty devices. What is a pty device? In BSD-influenced UNIXen, such as Linux or OS X, a pty is a "pseudoterminal". In other words, it's a terminal device that isn't attached to a physical terminal. If you've used a terminal program in Linux or OS X, you've probably used a pty without realizing it. GUI Terminal emulators, such as xterm, GNOME Terminal, and Terminal.app often use a pty device behind the scenes to communicate with the OS.

    What does this mean for us? It means if we're running Ruby on UNIX, we have the ability to start our subprocesses inside a virtual terminal. We can then read from and write to that terminal as if our program were a user sitting in front of a terminal, typing in commands and reading responses.

    Here's how it's used:

    puts "7. PTY"
    require 'pty'
    PTY.spawn(RUBY, '-r', THIS_FILE, '-e', 'hello("PTY", true)') do
      |output, input, pid|
      input.write("hello from parent\n")
      buffer = ""
      output.readpartial(1024, buffer) until buffer =~ /DONE/
      buffer.split("\n").each do |line|
        puts "[parent] output: #{line}"
      end
    end
    puts "---"
    

    And here is the output:

    7. PTY
    [parent] output: [child] Hello from PTY
    [parent] output: hello from parent
    [parent] output: [child] Standard input contains: "hello from parent"
    [parent] output: [child] Hello, standard error
    [parent] output: [child] DONE
    ---
    

    There are a few of points to note about this code. First, we don't need to call #close_write or #flush on the process input handle. However, the newline at the end of "Hello from parent" is essential. By default, UNIX terminal devices buffer input until they see a newline. If we left off the newline, the subprocess would never finish waiting for input.

    Second, because the subprocess is running asynchronously and independently from the parent process, we have no way of knowing exactly when it has finished reading input and producing output of its own. We deal with this by buffering output until we see a marker ("DONE").

    Third, you may notice that "hello from parent" appears twice in the output - once as part of the parent process output, and once as part of the child output. That's because another default behaviour for UNIX terminals is to echo any input they receive back to the user. This is what enables you to see what you've just typed when working at the command line.

    You can alter these default terminal device behaviours using the Ruby "termios" gem.

    Note that both STDOUT and STDERR were captured in the subprocess output. From the perspective of the pty user, standard output and standard error streams are indistinguishable - it's all just output. That means using pty is probably the only way to run a subprocess and capture standard error and standard output interleaved in the same way we would see if we ran the process manually from a terminal window. Depending on the application, this may be a feature or a drawback.

    You can execute PTY.spawn() without a block, in which case it returns an array of output, input, and PID. If you choose to experiment with this style of calling PTY.spawn(), be aware that you may need to rescue the PTY::ChildExited exception, which is thrown whenever the child process finally exits.

    If you're interested in reading more code which uses the pty library, the Standard Library also includes a library called "expect.rb". expect.rb is a basic Ruby reimplementation of the classic "expect" utility written using pty.

    Method #8: Shell

    More obscure even than the pty library is Ruby's Shell library. Shell is, to my knowledge, totally undocumented and rarely used. Which is a shame, because it implements some interesting ideas.

    Shell is an attempt to emulate a basic UNIX-style shell environment as an internal DSL within Ruby. Shell commands become Ruby methods, command-line flags become method parameters, and IO redirection is accomplished via Ruby operators.

    Here's an invocation of our standard example subprocess using Shell:

    puts "8. Shell"
    require 'shell'
    Shell.def_system_command :ruby, RUBY
    shell = Shell.new
    input  = 'Hello from parent'
    process = shell.transact do
      echo(input) | ruby('-r', THIS_FILE, '-e', 'hello("shell.rb", true)')
    end
    output = process.to_s
    output.split("\n").each do |line|
      puts "[parent] output: #{line}"
    end
    puts "---"
    

    And here is the output:

    8. Shell
    [child] Hello, standard error
    [parent] output: [child] Hello from shell.rb
    [parent] output: [child] Standard input contains: "Hello from parent"
    [parent] output: [child] DONE
    ---
    

    We start by defining the Ruby executable as a shell command by calling Shell.def_system_command. Then we instantiate a new Shell object. We construct the subprocess within a Shell#transact block. To have the process read a string from the parent process, we set up a pipeline from the echo built-in command to the Ruby invocation. Finally, we ensure the process is finished and collect its output by calling #to_s on the transaction.

    Note that the child process' STDERR stream is shared with the parent, not captured as part of the process output.

    There is a lot going on here, and it's only a very basic example of Shell's capabilities. The Shell library contains many Ruby-friendly reimplementations of common UNIX userspace commands, and a lot of machinery for coordinating pipelines of concurrent processes. If your interest is piqued I recommend reading over the Shell source code and experimenting within IRB. A word of caution, however: the Shell library isn't maintained as far as I know, and I ran into a couple of outright bugs in the process of constructing the above example. It may not be suitable for use in production code.

    Conclusion

    In this article we've looked at three Ruby standard libraries for executing subprocesses. In the next and final article we'll examine some publicly available Rubygems that provide even more powerful tools for starting, stopping, and interacting with subprocesses within Ruby.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on October 12th, 2009 by Avdi in Development, Ruby and tagged , , , , , .

  • Announcing Caliper

    by Avdi

    If you're anything like us, you recognize the usefulness of code quality metrics. But configuring metrics tools and linking them to your Continuous Integration system is a chore you may not have found time for. Here at Devver we're all about taking the pain out of common development chores. Which is why we're pleased to announce a new Devver service: Caliper.

    Caliper is the easy way to get code metrics - like cyclomatic complexity and code duplication - about your project. Just go to http://devver.net/caliper. Enter the public Git repository URL of a Ruby project. In a few moments, we'll give you a full set of metrics, generated by the awesome metric_fu gem.


    If your project is hosted on Github, we can even re-generate metrics automatically every time someone makes a commit. Just add the URL http://api.devver.net/github to your project's Post-Receive Hooks.

    Caliper is currently an Alpha product. We will be be rapidly adding features and refinements over the next few weeks. If you have a problem, a question, or a feature request, please let us know!

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on October 9th, 2009 by Avdi in Devver and tagged , , , , , , , .

  • Unit Testing Filesystem Interaction

    by Ben

    Like most Rubyists, I write unit tests to verify the non-trivial parts of my code. I also try to use mocks and stubs to stub out interactions with systems external to my code, like network services.

    For the most part, this works fine. But I've always struggled to find a good way to test interaction with the filesystem (which can often be non-trivial and therefore should be tested). On the one hand, the filesystem could be considered "external" and mocked out. But on the other hand, the filesystem is accessible when the tests run. In this way, the filesystem is sort of like a local database - it could be mocked out, but it doesn't have to be, and there are tradeoffs to both approaches.

    Over the past year or so, I've tried out a few approaches for testing interactions with the filesystem, each of which I'll explain below. Since none of the approaches met my needs, Avdi and I built a new testing library, which I'll introduce below.

    Mocking the file system.

    Sometimes, it is simplest to just mock the interaction with the filesystem. This works well for single calls to methods like File#read or File#exist? (these examples use Mocha):

    File.stubs(:read).returns("file contents")
    File.stubs(:exist?).returns(true)
    

    However, this approach breaks down when you want to test more complex code, which, of course, is the code you're more likely to want to test thoroughly. For instance, imagine trying to set up mocks/stubs for the following method (which atomically rewrites the contents of a file):

    require 'tempfile'
    
    class Rewriter
    
      def rewrite_file!(target_path)
        backup_path = target_path + '.bak'
        FileUtils.mv(target_path, backup_path)
        Tempfile.open(File.basename(target_path)) do |outfile|
          File.open(backup_path) do |infile|
            infile.each_line do |line|
              outfile.write(yield(line))
            end
          end
          outfile.close
          FileUtils.cp(outfile.path, target_path)
        end
      rescue Exception
        if File.exist?(backup_path)
          FileUtils.mv(backup_path, target_path)
        end
        raise
      end
    
    end
    

    Now imagine setting up those same mocks/stubs for each of the five or so tests you'd want to test that method. It gets messy.

    Even more importantly, mocking/stubbing out methods ties your tests to a specific implementation. For instance, if you use the above stub (File.stubs(:read).returns("file contents")) in your test and then refactor your implementation to use, say, File.readlines, you'll have to update your tests. No good.

    MockFS

    MockFS is a library that mocks out the entire filesystem. It allows you write test code like this:

    require 'test/unit'
    require 'mockfs'
    
    class TestMoveLog < Test::Unit::TestCase
    
      def test_move_log
        # Set MockFS to use the mock file system
        MockFS.mock = true
    
        # Fill certain directories
        MockFS.fill_path '/var/log/httpd/'
        MockFS.fill_path '/home/francis/logs/'
    
        # Create the access log
        MockFS.file.open( '/var/log/httpd/access_log', File::CREAT ) do |f|
          f.puts "line 1 of the access log"
        end
    
        # Run the method under test
        move_log
    
        # Test that it was moved, along with its contents
        assert( MockFS.file.exist?( '/home/francis/logs/access_log' ) )
        assert( !MockFS.file.exist?( '/var/log/httpd/access_log' ) )
        contents = MockFS.file.open( '/home/francis/logs/access_log' ) do |f|
          f.gets( nil )
        end
        assert_equal( "line 1 of the access log\n", contents )
      end
    end
    

    Although I suspect MockFS would be a great fit for some projects, I ended up running into issues.

    First of all, it depends on a library (extensions) that can have strange monkey-patching conflicts with other libraries. For example, compare this:

    require 'faker'
    puts [].respond_to?(:shuffle) # true
    

    to this:

    require 'extensions/all'
    require 'faker'
    puts [].respond_to?(:shuffle) # false
    

    Secondly, as you'll notice in the above example, using MockFS requires you to use methods like MockFS.file.exist? instead of just File.exist?. This works fine if you're only testing your own code. However, if your code calls any libraries that use filesystem methods, MockFS won't work.

    (Note: There is a way to mock out the default filesystem methods, but it's experimental. From the MockFS documentation:

    "Reading the testing example above, you may be struck by one thing: Using MockFS requires you to remember to reference it everywhere, making calls such as MockFS.file_utils.mv instead of just FileUtils.mv. As another option, you can use File, FileUtils, and Dir directly, and then in your tests, substitute them by including mockfs/override.rb. I'd recommend using these with caution; substituting these low-level classes can have unpredictable results. ")

    All that said, MockFS is probably your best option if you're only testing your code and you want to mock out files that you can't actually interact with - for instance, if you need to test that a method reads/writes a file in /etc (although for the sake of testability, it's generally good to avoid hardcoding fully-qualified paths in your code).

    FakeFS is another library that uses this approach. I haven't used it personally, but it looks quite nice.

    Creating temp files and directories (with Construct)

    Besides mocking the filesystem, another option is to have tests interact with actual files and directories on disk. The advantages are that the test code can be simpler to write and you don't have to use any special filesystem methods.

    Of course, as always, you want the test itself to contain all the relevant setup and teardown - you don't want your tests to depend upon some set of files that have no explicit connection to the test itself (or create files that aren't cleaned up).

    To make this easy, we created a new library called Construct. Construct makes test setup simple by providing helpers to create temporary files and directories. It takes care of the cleanup by automatically deleting the directories and files that are created within the test. And because it creates regular files and directories, you can use plain old Ruby filesystem methods in your code and tests.

    To install Construct, simply run:

    # gem install devver-construct --source http://gems.github.com
    

    Using Construct, you can write code like this:

    require 'construct'
    
    class ExampleTest < Test::Unit::TestCase
      include Construct::Helpers
    
      def test_example
        within_construct do |construct|
          construct.directory 'alice/rabbithole' do |dir|
            dir.file 'white_rabbit.txt', "I'm late!"
            assert_equal "I'm late!", File.read('white_rabbit.txt')
          end
        end
      end
    
    end
    

    Let's look at each line in more detail.

        within_construct do |construct|
    

    When you call within_construct, a temporary directory is created. All files and directories are, by default, created within that temporary directory and the temporary directory is always deleted before within_construct completes.

    The block argument (construct) is a Pathname object with some additional methods (#directory and #file, which I'll explain below). You can use this object to get the path to the temporary directory created by Construct and easily create files and directories.

    Note that, by default, the working directory is changed to the temp dir within the block provided to within_construct.

          construct.directory 'alice/rabbithole' do |dir|
    

    Here we are using the construct object to create a new directory within the temp directory. As you can see, you can create nested directories like alice/rabbithole in one step. The block argument (dir) is again a Pathname object with the same added functionality noted above.

    Just like before, the working directory is changed to the newly created directory (in this case, alice/rabbithole) within the block.

            dir.file 'white_rabbit.txt', "I'm late!"
    

    Here we use the dir object to create a file. In this case, the file will be empty. However, it's easy to provide file contents using either an optional parameter or the return value of the supplied block:

    within_construct do |construct|
      construct.file('foo.txt','Here is some content')
      construct.file('bar.txt') do
      <<-EOS
      The block will return this string, which will be used as the content.
      EOS
      end
    end
    

    As a more real-world example, here's how you could use Construct to start testing the #rewrite_file! method we looked at before:

    require 'test/unit'
    require 'construct'
    require 'shoulda'
    
    class RewriterTest < Test::Unit::TestCase
      include Construct::Helpers
    
      context "#rewrite_file!" do
    
        should "alter each line in file" do
          within_construct do |c|
            c.file('bar/foo.txt',"a\nb\nc\n")
            Rewriter.new.rewrite_file!('bar/foo.txt') do |line|
              line.upcase
            end
            assert_equal "A\nB\nC\n", File.read('bar/foo.txt')
          end
        end
    
        should "not alter file if exception is raised" do
          within_construct do |c|
            c.file('foo.txt', "1\n2\nX\n")
            assert_raises ArgumentError do
              Rewriter.new.rewrite_file!('foo.txt') do |line|
                Integer(line)*2
              end
            end
            assert_equal "1\n2\nX\n", File.read('foo.txt')
          end
        end
    
      end
    
    end
    

    You can learn more at the project page (both the README and the tests have more examples).

    (As an aside, since Construct changes the working directory, it doesn't play nicely with ruby-debug. Specifically, if you place a breakpoint within a block, you'll see the message "No sourcefile available for test/unit/foo_test.rb" and you won't be able to view the source. If anyone knows an easy way to make Dir.chdir work with ruby-debug, I'd very much appreciate some help!)

    Conclusion

    We've been moving our filesystem tests over to using Construct and so far have found it to be very useful. How do you test interactions with the filesystem? Do you use one of the above approaches, or something else? Or do you skip testing the filesystem altogether?

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on August 25th, 2009 by Ben in Hacking, Testing and tagged , , .

  • Announcing Devver as a Lone Star Ruby Conference Sponsor

    by Dan

    We are very happy to be a sponsor of LSRC. I am especially excited because that means I get to attend the event. I am looking forward to getting a chance to meet another Ruby community as I have never been to Austin Texas, and it seems like there are a lot of exciting things going on with the Ruby community. Find me and come by to talk about Ruby, testing, or Devver. Devver is also currently hiring, so if you are attending the conference and interested in highly distributed Ruby systems, definitely come talk to us. It is great to get to participate in events like these and spend time with the amazing Ruby community which is so supportive of new ideas, good code and testing, and startups.

    Check out some of the great things that will be going on at Lone Star Ruby Conf this year.

    I am particularly excited about:

    • Mike Subelsky: Ruby for Startups: Battle Scars and Lessons Learned
    • Larry Diehl: Dataflow: Declarative concurrency in Ruby
    • Ian Warshak: Rails in the Cloud
    • Jeremy Hinegardner: Playing nice with others. -- Tools for mixed language environments.
    • Evan Light: TDD: More than just "testing"
    • Jake Scruggs: What's the Right Level of Testing?
    • Corey Donohoe, atmos: think simple
    • Pradeep Elankumaran: Fast and Scalable Front/Back-end Services using Ruby and XMPP
    • Danny Blitz: Herding Tigers - Software and the Art of War
    • Looking forward to meeting everyone in Austin, shoot me an email at dan@devver.net or message me on twitter @danmayer so we can meet up at the conference in person.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on July 20th, 2009 by Dan in Devver, Ruby and tagged .

  • A command-line prompt with timeout and countdown

    by Avdi

    Have you ever started a long operation and walked away from the computer, and come back half an hour later only to find that the process is hung up waiting for some user input? It's a sub-optimal user experience, and in many cases it can be avoided by having the program choose a default if the user doesn't respond within a certain amount of time. One example of this UI technique in the wild is powering off your computer - most modern operating systems will pop up a dialogue to confirm or cancel the shutdown, with a countdown until the shutdown proceeds automatically.

    This article is about how to achieve the same effect in command-line programs using Ruby.

    Let's start with the end result. We want to be able to call our method like this:

    puts ask_with_countdown_to_default("Do you like pie?", 30.0, false)
    

    We pass in a question, a (possibly fractional) number of seconds to wait, and a default value. The method should prompt the user with the given question and a visual countdown. If the user types 'y' or 'n', it should immediately return true or false, respectively. Otherwise when the countdown expires it should return the default value.

    Here's a high-level implementation:

    def ask_with_countdown_to_default(question, seconds, default)
      with_unbuffered_input($stdin) do
        countdown_from(seconds) do |seconds_left|
          write_then_erase_prompt(question, seconds_left) do
            wait_for_input($stdin, seconds_left % 1) do
              case char = $stdin.getc
              when ?y, ?Y then return true
              when ?n, ?N then return false
              else                  # NOOP
              end
            end
          end
        end
      end
      return default
    ensure
      $stdout.puts
    end                             # ask_with_countdown_to_default
    

    Let's take it step-by-step.

    By default, *NIX terminals operate in "canonical mode", where they buffer a line of input internally and don't send it until the user hits RETURN. This is so that the user can do simple edits like backspacing and retyping a typo. This behavior is undesirable for our purposes, however, since we want the prompt to respond as soon as the user types a key. So we need to temporarily alter the terminal configuration.

      with_unbuffered_input($stdin) do
    

    We use the POSIX Termios library, via the ruby-termios gem, to accomplish this feat.

    def with_unbuffered_input(input = $stdin)
      old_attributes = Termios.tcgetattr(input)
      new_attributes = old_attributes.dup
      new_attributes.lflag &= ~Termios::ECHO
      new_attributes.lflag &= ~Termios::ICANON
      Termios::tcsetattr(input, Termios::TCSANOW, new_attributes)
    
      yield
    ensure
      Termios::tcsetattr(input, Termios::TCSANOW, old_attributes)
    end                             # with_unbuffered_input
    

    POSIX Termios defines a set of library calls for interacting with terminals. In our case, we want to disable some of the terminal's "local" features - functionality the terminal handles internally before sending input on to the controlling program.

    We start by getting a snapshot of the terminal's current configuration. Then we make a copy for our new configuration. We are interested in two flags: "ECHO" and "ICANON". The first, ECHO, controls whether the terminal displays characters that the user has types. The second controls canonical mode, which we explained above. After turning both flags off, we set the new configuration and yield. After the block is finished, or if an exception is raised, we ensure that the original terminal configuration is reinstated.

    Now we need to arrange for a countdown timer.

        countdown_from(seconds) do |seconds_left|
    

    Here's the implementation:

    def countdown_from(seconds_left)
      start_time   = Time.now
      end_time     = start_time + seconds_left
      begin
        yield(seconds_left)
        seconds_left = end_time - Time.now
      end while seconds_left > 0.0
    end                             # countdown_from
    

    First we calculate the wallclock time at which we should stop waiting. Then we begin looping, yielding the number of seconds left, and then when the block returns recalculating the number. We keep this up until the time has expired.

    Next up is writing, and re-writing, the prompt.

          write_then_erase_prompt(question, seconds_left) do
    

    This method is implemented as follows:

    def write_then_erase_prompt(question, seconds_left)
      prompt_format = "#{question} (y/n) (%2d)"
      prompt = prompt_format % seconds_left.to_i
      prompt_length = prompt.length
      $stdout.write(prompt)
      $stdout.flush
    
      yield
    
      $stdout.write("\b" * prompt_length)
      $stdout.flush
    end                             # write_then_erase_prompt
    

    We format and print a prompt, flushing the output to insure that it is displayed immediately. The prompt includes a count of the number of seconds remaining until the query times out. In order to make it a nice visually consistent length, we use a fixed-width field for the countdown ("%2d"). Note that we don't use puts to print the prompt - we don't want it to advance to the next line, because we want to be able to dynamically rewrite the prompt as the countdown proceeds.

    After we are done yielding to the block, we erase the prompt in preparation for the next cycle. In order to erase it we create and output string of backspaces ("\b") the same length as the prompt.

    Now we need a way to wait until the user types something, while still periodically updating the prompt.

            wait_for_input($stdin, seconds_left % 1) do
    

    We pass wait_for_input an input stream and a (potentially fractional) number of seconds to wait. In this case we only want to wait until the next second-long "tick" so that we can update the countdown. So we pass in the remainder of dividing seconds_left by 1. E.g. if seconds_left was 5.3, we would set a timeout of 0.3 seconds. After 3/10 of a second of waiting for input, the wait would time out, the prompt would be erased and rewritten to show 4 seconds remaining, and then we'd start waiting for input again.

    Here's the implementation of wait_for_input:

    def wait_for_input(input, timeout)
      # Wait until input is available
      if select([input], [], [], timeout)
        yield
      end
    end                             # wait_for_input
    

    We're using Kernel#select to do the waiting. The parameters to #select are a set of arrays - one each for input, output, and errors. We only care about input, so we pass the input stream in the first array and leave the others blank. We also pass how long to wait until timing out.

    If new input is detected, select returns an array of arrays, corresponding to the three arrays we passed in. If it times out while waiting, it returns nil. We use the return value to determine whether to execute the given block or note. If there is input waiting we yield to the block; otherwise we just return.

    While it takes some getting used to, handling IO timeouts with select is safer and more reliable than using the Timeout module. And it's less messy than rescuing Timeout::Error every time a read times out.

    Finally, we need to read and interpret the character the user types, if any.

              case char = $stdin.getc
              when ?y, ?Y then return true
              when ?n, ?N then return false
              else                  # NOOP
              end
    

    If the user types 'y' or 'n' (or uppercase versions of the same), we return true or false, respectively. Otherwise, we simply ignore any characters the user types. Typing characters other than 'y' or 'n' will cause the loop to be restarted.

    Note the use of character literals like ?y to compare against the integer character code returned by IO#getc. We could alternately use Integer#chr to convert the character codes into single-character strings, if we wanted.

    Wrapping up, we make sure to return the default value should the timeout expire without any user input; and we output a newline to move the cursor past our prompt.

      return default
    

    And there you have it; a yes/no prompt with a timeout and a visual countdown. Static text doesn't really capture the effect, so rather than include sample output I'll just suggest that you try the code out for yourself (sorry, Windows users, it's *NIX-only).

    Full source for this article at: http://gist.github.com/148765

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on July 16th, 2009 by Avdi in Ruby, Tips & Tricks and tagged , , , , .

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

    by Avdi

    In the previous article we looked at some basic methods for starting subprocesses in Ruby. One thing all those methods had in common was that they didn't permit a lot of communication between parent process and child. In this article we'll examine a few built-in Ruby methods which give us the ability to have a two-way conversation with our subprocesses.

    The complete source code for this article can be found at http://gist.github.com/146199.

    Method #4: Opening a pipe

    As you know, the Kernel#open method allows you to open files for reading and writing (and, with addition of the open-uri library, HTTP sockets as well). What you may not know is that Kernel.open can also open processes as if they were files.

      puts "4a. Kernel#open with |"
      cmd = %Q<|#{RUBY} -r#{THIS_FILE} -e 'hello("open(|)", true)'>
      open(cmd, 'w+') do |subprocess|
        subprocess.write("hello from parent")
        subprocess.close_write
        subprocess.read.split("\n").each do |l|
          puts "[parent] output: #{l}"
        end
        puts
      end
      puts "---"
    

    By passing a pipe ("|") as the first character in the command, we signal to open that we want to start a process, not open a file. For a command, we're starting another Ruby process and calling our trusty hello method (see the first article or the source code for this article for the definition of the hello method RUBY and THIS_FILE constants).

    open yields an IO object which enables us to communicate with the subprocess. Anything written to the object is piped to the process' STDIN, and the anything the process writes to its STDOUT can be read back as if reading from a file. In the example above we write a line to the child, read some text back from the child, and then end the block.

    Note the call to close_write on line 5. This call is important. Because the OS buffers input and output, it is possible to write to a subprocess, attempt to read back, and wait forever because the data is still sitting in the buffer. In addition, filter-style programs typically wait until they see an EOF on their STDIN to exit. By calling close_write, we cause the buffer to be flushed and an EOF to be sent. Once the subprocess exits, its output buffer wil be flushed and any read calls on the parent side will return.

    Also note that we pass "w+" as the file open mode. Just as with files, by default the IO object will be opened in read-only mode. If we want to both write to and read from it, we need to specify an appropriate mode.

    Here's the output of the above code:

    4a. Kernel#open with |
    [child] Hello, standard error
    [parent] output: [child] Hello from open(|)
    [parent] output: [child] Standard input contains: "hello from parent"
    
    ---
    

    Another way to open a command as an IO object is to call IO.popen:

      puts "4b. IO.popen"
      cmd = %Q<#{RUBY} -r#{THIS_FILE} -e 'hello("popen", true)'>
      IO.popen(cmd, 'w+') do |subprocess|
        subprocess.write("hello from parent")
        subprocess.close_write
        subprocess.read.split("\n").each do |l|
          puts "[parent] output: #{l}"
        end
        puts
      end
      puts "---"
    

    This behaves exactly the same as the Kernel#open version. Which way you choose to use is a matter of preference. The IO.popen version arguably makes it a little more obvious what is going on.

    Method #5: Forking to a pipe

    This is a variation on the previous technique. If Kernel#open is passed a pipe followed by a dash ("|-") as its first argument, it starts a forked subprocess. This is like the previous example except that instead of executing a command, it forks the running Ruby process into two processes.

      puts "5a. Kernel#open with |-"
      open("|-", "w+") do |subprocess|
        if subprocess.nil?             # child
          hello("open(|-)", true)
          exit
        else                        # parent
          subprocess.write("hello from parent")
          subprocess.close_write
          subprocess.read.split("\n").each do |l|
            puts "[parent] output: #{l}"
          end
          puts
        end
      end
      puts "---"
    

    Both processes then execute the given block. In the child process, the argument yielded to the block will be nil. In the parent, the block argument will be an IO object. As before, the IO object is tied to the forked process' standard input and standard output streams.

    Here's the output:

    5a. Kernel#open with |-
    [child] Hello, standard error
    [parent] output: [child] Hello from open(|-)
    [parent] output: [child] Standard input contains: "hello from parent"
    
    ---
    

    Once again, there is an IO.popen version which does the same thing:

      puts "5b. IO.popen with -"
      IO.popen("-", "w+") do |subprocess|
        if subprocess.nil?             # child
          hello("popen(-)", true)
          exit
        else                        # parent
          subprocess.write("hello from parent")
          subprocess.close_write
          subprocess.read.split("\n").each do |l|
            puts "[parent] output: #{l}"
          end
          puts
        end
      end
      puts "---"
    

    Applications and Caveats

    The techniques we've looked at in this article are best suited for "filter" style subprocesses, where we want to feed some input to a process and then use the output it produces. Because of the potential for deadlocks mentioned earlier, they are less suitable for running highly interactive subprocesses which require multiple reads and responses.

    open/popen also do not give us access to the subprocess' standard error (STDERR) stream. Any output error generated by the subprocesses will print the same place that the parent process' STDERR does.

    In the upcoming parts of the series we'll look at some libraries which overcome both of these limitations.

    Conclusion

    In this article we've explored two (or four, depending on how you count it) built-in ways of starting a subprocess and communicating with it as if it were a file. In part 3 we'll move away from built-ins and on to the facilities provided in Ruby's Standard Library for starting and controlling subprocesses.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on July 13th, 2009 by Avdi in Ruby, Tips & Tricks and tagged , .

  • Devver adds Postgres and SQLite database support

    by Dan

    We are working hard to quickly expand our compatibility on Ruby projects. With that goal driving us, we are happy to announce support for Postgres and SQLite databases. With the addition of these database options, along with our existing support for MySQL, Devver now supports all of the most popular databases commonly used with Ruby. These three databases are the default databases tested against ActiveRecord and we expect will cover the majority of the Ruby community.

    To begin working with Postgres or SQLite on Devver all you need to do is have a database.yml with the test environment set to the adapter of your choice. If we don't support your favorite database, you can still request a beta invite and let us know which database you want us to support. If we just added support for your database, perhaps we can speed up your project on Devver, so request a beta invite.

    Devver Caliper: Hosted metric_fu for your Ruby project.
    Get set up in under a minute

    Posted on July 6th, 2009 by Dan in Development, Devver, Ruby, Testing and tagged , , .