Blog

Sep 01 2009

Comments

RunCodeRun Basic Plan Now Available

We've been building your open source projects for about a year now. Thanks to the experience we've gained from our wonderful commercial beta users you too can let us take care of your continuous integration needs, no matter what they are -- as long as you use GitHub.

The Premium and Walk Among the Gods plans, announced earlier this month provide a dedicated server for each account. We're pleased to announce that the RunCodeRun Basic Plan is also available. Targeted at solo developers, the Basic Plan costs $19 per month and supports up to three private projects. Unlike our other plans, the Basic Plan private builds run on shared RunCodeRun build resources.

With three plans to choose from, we know that one of them is just right for you. Sign up and focus on delivering valuable software instead of on maintaining CI servers.

Aug 25 2009

Comments

Tarantula Supports Ruby 1.9

We're excited about Ruby 1.9. It's fast and stable, and brings nice improvements for programmers. But there are obstacles to adoption---mostly gems, plugins, and tools that don't support 1.9 yet.

So we're working on the things Relevance maintains, to get them on board. We're doing it by dog-fooding: We've got one customer project that we're actively testing on both 1.8 and 1.9 (although deployment is still on 1.8 for the moment). That's helping us see where the holes are, so we can fix them. We're already working on Ruby 1.9 support for RunCodeRun, and investigating the best way to interface rcov with 1.9.

Now one more piece of the puzzle is in place: on our last open-source Friday, Chad upgraded Tarantula to support Ruby 1.9. If you're trying out Rails on 1.9 (or even if you aren't), get the latest version (0.2.1) of tarantula from github:

gem install --source http://gems.github.com relevance-tarantula

Then add the plugin to your app and add some fuzz-testing goodness to it!

Aug 19 2009

Comments

The Longevity of "Enterprise" Tools

During the recent erubycon conference, there was a lot of twitter traffic from attendees about how great the conference was. One who did not attend wondered if a conference discussing "Ruby in the enterprise" was wishful thinking or reality?.

Sorry if this disappoints anyone, but there was almost no wishful thinking, and a lot of reality. But there's a third option: planning next moves. There was a lot of discussion about how to promote Ruby effectively in enterprises that aren't already using it---not because Ruby is the be-all, end-all of programming (it's not) but because we have all found it helpful across a wide variety of programming tasks, and we think it is a good antidote for the more common, "enterprisey" tools that often bring more bad than good to a project.

During a panel discussion right after my keynote (Why "Enterprise Tools" are Bad for the Enterprise), someone asked "What do we say to a manager who doesn't trust open-source tools to still be around in a few years?"

Something snapped in me---I'd heard that same question one too many times. Commercial tools are available solely based on their customers. If the tool gets enough customers, the tool continues. Otherwise, it will all disappear. And it must disappear, given the nature of the software business.

There are three scenarios (with variations) for new software development tools, and all are common:

  • get enough customers to be profitable, and continue for many years.
  • get enough customers to look viable, get purchased and see the tool squashed or sidelined by the new owners.
  • get just a few customers, and have your board shut you down.

When you choose a commercial tool, you're making a bet that enough other people will purchase it to make it viable, and that it won't be purchased and shut down by a competitor.

When you choose open-source tools, there is no monetary pressure on the toolmaker to continue; only community pressure. It's much easier (and cheaper) to bring community pressure. Additionally, even if the toolmaker chooses not to continue, the community can keep the tool alive. Many open-source development tools that have long since dropped from most people's radar (think Tcl, for example) still have thriving communities that actively support the tools and help each other with difficulties. With a proprietary tool, the legal structures surrounding it virtually guarantee that the tool will die if the vendor loses interest.

In every respect, open source tools have a better chance of surviving than their commercial cousins. Sure, some of those commercial tools will be viable for a long time, but most won't. Whereas most open source tools will be around for a long time. Open source tools actually diminish risk, not enhance it.

Anyway, getting back to the panel question at erubycon: I decided it was time for action, because this is such an easy point to refute with just a little preparation.

So I volunteered to play scribe during the party that evening, and I spent much of the party sitting in a chair with a pad and pen, while attendees helped me remember "enterprise tools" that have come and gone. We built a list of commercial, "enterprise" software development tools of one sort or another that were commercially viable no longer ago than 1995 (when Java first hit the scene) but which are now "dead". ("Dead", for purposes of this list, means either that there is no longer commercial support for the tool, or that there has been no significant new development on the tool in the past five years.)

It's a long list.

The point of this is not to prove that open-source tools are better than proprietary tools (obviously I think they are in most cases, but this list won't prove it either way). The point is to show clearly that there is no guarantee that a tool will still be around and supported after a few years, proprietary or not.

I'm sure there are other tools that we didn't think of. I'm equally sure that some of the tools currently on the list are actually alive; the fact that attendees at erubycon haven't heard of a tool for a while doesn't mean it isn't still being maintained and supported somewhere. (Here's a good example. I thought of a tool that seemed like an open-and-shut case for this: NetDynamics, the original Java application server. A little research shows that it was purchased by Sun, and some of its code formed the basis for the iPlanet application server, which was later called Sun ONE, and then the Sun Java Enterprise System, and is now called GlassFish. Nevertheless, I strongly doubt that old apps written for NetDynamics would run on GlassFish, and I'd be interested to know whether there was ever a clear migration path for NetDynamics customers.)

So I'm starting the task of double-checking that list. If you're interested in helping, send me email! I'll blog about the results here.

Aug 12 2009

Comments

Rifle-Oriented Programming with Clojure

Any comparison of hot JVM languages is likely to note that “Clojure is not object-oriented.” This is true, but it may lead you to the wrong conclusions. It’s a little like saying that a rifle is not arrow-oriented. In this article, you will see some of the ways that Clojure addresses the key concerns of OO: encapsulation, polymorphism, and inheritance.

This is a whirlwind tour, and we won't have time to cover the full details of all the Clojure code you will see. When we are done, I hope you will decide to explore for yourself. You can download and start using Clojure by following the instructions on the getting started page.

Just Enough Clojure Syntax

Clojure has vectors, which are accessed by integer indexes:

  [1 2 3 4]
  -> [1 2 3 4]

  (get [:a :b :c :d :e] 2)
  -> :c

In the preceding example, the initial `[1 2 3 4]` is input that you enter at the Read-Eval-Print Loop (REPL). The `->` indicates the response from the REPL.

Clojure has maps, which are key/value collections:

  {:fname "Stu", :lname "Halloway"}
  -> {:fname "Stu", :lname "Halloway"}

Sets contain a set of values, and their literal form is preceded with a hash. Here is the set of English vowels, using backslash to introduce a character literal:

  #{\a \e \i \o \u}
  -> #{\a \e \i \o \u}

Lists are singly-linked lists, and are enclosed with parentheses. Lists are special: Not only are they data, they also act as the syntax for invoking functions. The list below invokes the plus (`+`) function:

  (+ 1 2 3 4 5)
  -> 15

Collections themselves act as functions. They take an argument which is the key/index to look up:

  ([:a :b :c :d :e] 2)
  -> :c

  ({:name "Stu" :ext 101} :name)
  -> "Stu"

Enough syntax, let's get started.

Encapsulation

Encapsulation is the hiding of implementation details so that clients of your code do not accidentally become dependent on them. In object-oriented languages, this is usually done at the class level. A class has public methods, private implementation details, and various other scopes in between.

Clojure accomplishes the purposes of encapsulation in three ways: closures, namespaces, and immutability.

Closures

A closure closes over (remembers) the environment at the time it was created. For example, the function `make-counter` below closes over the initial value passed via `init-val`:

  (defn make-counter [init-val] 
    (let [c (atom init-val)] #(swap! c inc)))

Let’s break this down:

  • `defn` defines a new function, named `make-counter`, that takes a single argument `init-val`.
  • The `let` binds the name `c` to a new `atom`.
  • The `atom` creates a threadsafe, deadlock-proof mutable reference to a value.
  • The octothorpe (`#`) prefix introduces an anonymous function
  • The call to `swap!` updates the value referenced by `c` by calling `inc` on it.
  • The value of the let is the value of its last expression. This `let` returns a function that increments a counter, which is then the return value of `make-counter`.

The atom `c` is private to the function returned by `make-counter`. The only public thing you can do is increment it by one:

  (def c (make-counter))
  -> #'user/c

  (c)
  -> 1

  (c)
  -> 2

  (c)
  -> 3

The counter example returned a single function, but nothing stops you from returning multiple functions. These multiple functions can then share private state. The new version of `make-counter` below returns two functions: one to increment the counter, and one to reset it.

  (defn make-counter [init-val] 
    (let [c (atom init-val)] 
      {:next #(swap! c inc)
       :reset #(reset! c init-val)}))

This new `make-counter` returns a map whose `:next` value increments the counter, and whose `:reset` value resets it:

  (def c (make-counter 10))
  -> #'user/c

  ((c :next))
  -> 11

  ((c :next))
  -> 12

  ((c :reset))
  -> 10

Why the double parentheses above? Two functions calls: The inner function call looks up the appropriate function, and the outer one calls it.

Closing over data is far more general than the simplistic model offered by private, protected, public, friend, et al. in OO languages. By combining multiple lets and multiple return values from a function, you can create arbitrary encapsulation strategies.

Similar encapsulation possibilities are available in any language that supports closures. Douglas Crockford describes a similar idiom in JavaScript.

Namespaces

A Clojure namespace groups a set of related data and functions. Inside a namespace, a Clojure var can refer to a function or to data, and can be public or private.

For example, Chris Houser’s error-kit library implements a condition/restart system for Clojure.

  (with-handler
    (vec (map int-half [2 4 5 8]))
      (handle *number-error* [n]
        (continue-with 0))) 

In the code above, `with-handler`, `handle`, and `continue-with` are public vars of the `clojure.contrib.error-kit` namespace. The `int-half` is a demo function that blows up on odd inputs. When a `*number-error*` occurs, the handler causes execution to continue with the value 0. (Note how this is more flexible than try/catch exception handling, which cannot recover back into the middle of some operation.)

Internally, error-kit keeps track of available handlers and continues using these private vars:

  (defvar- *handler-stack* () 
    "Stack of bound handler symbols")
  (defvar- *continues* {} 
    "Map of currently available continue forms")

The trailing minus sign on the end of `defvar-` marks the vars as private. These vars are implementation details, and are invisible to code outside the `clojure.contrib.error-kit` namespace.

Immutability

In OO languages, another purpose of encapsulation is to prevent object A from modifying or corrupting the private data used by object B.

In Clojure, this problem does not exist. Data structures are immutable. They cannot possibly be corrupted, or changed in any way, period. You can write query functions that return “private” state, without any fear of data corruption.

Polymorphism

For our purposes here, polymorphism is the ability to choose a different method implementation based on the type of the caller. So for example:

  Flyer a = new Airplane();
  Flyer b = new Bird();
  a.fly();
  b.fly();

`a.fly()` and `b.fly()` do different things because they are called on different concrete types.

Clojure provides a generalization of polymorphism called multimethods. A multimethod definition begins with `defmulti`, and then has a name, plus a dispatch function that is used to select the actual implementation: To mimic polymorphism, simply dispatch on the `class` of the argument:

  (defmulti fly class)

Individual methods of a multimethod begin with `defmethod`, then the multimethod name, then the object that must match the dispatch function. Finally, you get the argument list in a vector, followed by the implementation of the method. For example:

  (defmethod fly Bird [b] (flap-wings b))
  (defmethod fly Airplane [a] (turn-propeller a))

Unlike polymorphism, multimethods do not limit you to dispatching on class. You can dispatch based on any arbitrary function of the method arguments. So for example, a bank account might have a `:type` entry that is used to determine the interest rate:

  (defmulti interest :type)
  (defmethod interest :checking [a] 0)
  (defmethod interest :savings [a] 0.05M)

The `:type` attribute is a convention, but nothing prevents you from dispatching on a different attribute, or even dispatching on more than one at the same time! For example, the `service-charge` multimethod below dispatches on two different facets of the same object: the object’s `account-level` (`::Basic` or `::Premium`) and its `:tag:` (`::Checking` or `::Savings`)

  (defmulti service-charge 
    (fn [acct] [(account-level acct) (:tag acct)]))
  (defmethod service-charge [::Basic ::Checking]   [_] 25)
  (defmethod service-charge [::Basic ::Savings]    [_] 10)
  (defmethod service-charge [::Premium ::Checking] [_] 0)
  (defmethod service-charge [::Premium ::Savings]  [_] 0)

The `_` is a legal name, and is used idiomatically to indicate that an argument will be ignored. (There is no need to even look at the argument, since all the work has been done in choosing which method to dispatch to!) This example also demonstrates two other concepts:

  • The double-colon prefix resolves a keyword in a namespace. This prevents name collisions among keywords, just as object-oriented langauges use namespaces to prevent name collisions between type names.
  • `account-level` is a function (not shown here), not a simple key lookup. It returns `::Premium` or `::Basic` based on the the account type and the current balance. Thus an account can dynamically change its account level as its balance changes.

As you can see, multimethods are far more general than polymorphism. Instead of being limited to type-based dispatch, multimethods can dispatch on any arbitrary function of an argument list. This allows programming models that more closely resemble reality: after all, what real-world entities are limited to a single type hierarchy, and forbidden to change types over time?

Inheritance

In OO languages, inheritance allows you to create a derived type that reuses the behavior of a base type. For example:

  class Person {
    String fullName() { /* impl details */ }
  }
  class Employee extends Person {
    AddressBookItem companyDirectoryEntry() { /* impl details */ }
  }

This kind of reuse is so natural in Clojure that it doesn’t even have a name. For example, here is a function that returns the full name of a person, based on first and last names:

  (defn full-name [p]
    (str (:first-name p) " " (:last-name p)))

Employees are like people, but have other properties and behaviors, such as a telephone extension. The `company-directory-entry` returns a vector of an employee's full name and telephone extension, like this:

  (defn company-directory-entry [p]
    [(full-name p) (:extension p)])

Notice that `company-directory-entry` “reuses” the person-ness of its argument `p` by calling `full-name` on it. There is no special inheritance ceremony required to set this up, you just call functions when you need them.

You can pass either a person or an employee to `full-name`. For `company-directory-entry`, though, you must have an employee. Or, more accurately, you must have something that resembles an employee, to the extent of having a `:first-name`, `:last-name`, and `:extension`. This is an example of duck typing: if it walks like a duck and quacks like a duck, we assume it is a duck, without asking it to present its `IDuck` papers.

Many Functions, Few Types

The example above demonstrates another negative consequence of idiomatic OO style: the over-specification of data types. The return value of `companyDirectoryEntry` is given its own unique type, `AddressBookItem`. Each new data type like `AddressBookItem` requires its own life-support system: constructors, accessors, `equals`, `hashCode`, and so on.

In Clojure, an address book item would simply be a vector or a map. No new types, and no life support system required. Moreover, an address book item can be manipulated with any of the large arsenal of functions in Clojure's sequence library.

To see the problem with overspecifying types, consider this method from the Apache Commons:

  // From Apache Commons Lang, http://commons.apache.org/lang/
  public static int indexOfAny(String str, char[] searchChars) {
      if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) {
    return -1;
      }
      for (int i = 0; i < str.length(); i++) {
    char ch = str.charAt(i);
    for (int j = 0; j < searchChars.length; j++) {
        if (searchChars[j] == ch) {
        return i;
        }
    }
      }
      return -1;
  }

The purpose of `indexOfAny` is to find the index of the first occurrence of one of the `searchChars` that appears in `str`. Note the unnecessary specificity of types: it works only with strings and character arrays.

Here's the Clojure version, using the sequence library's `map`, `iterate`, and `for` forms:

  (defn indexed [coll] (map vector (iterate inc 0) coll))
  (defn index-filter [pred coll]
    (when pred 
      (for [[idx elt] (indexed coll) :when (pred elt)] idx)))

Here is an example calling `index-filter`:

  (index-filter #{\a \e \i \o \u} "Lts f cnsnts nd n vwel")
  -> (20)

The expression above finds the index of the first vowel in the string "Lts f cnsnts nd n vwel", that is, 20. But `index-filter` is more general than the Commons version in several ways:

1. `index-filter` returns all the matches, not just one.

  (index-filter #{\a \e \i \o \o} "The quick brown fox")
  -> (2 6 12 17)

2. `index-filter` works with any sequence, not just a string of characters. For example, the call below works against a range of integers:

  (index-filter #{2 3 5 7} (range 6))
  -> (2 3 5)

3. `index-filter` works with any predicate, not just a test against a character array. In the example below, the predicate is an anonymous function that tests for strings longer than three characters:

  (index-filter #(> (.length %) 3) ["The" "quick" "brown" "fox"])
  -> (1 2)

That is a lot of extra power, especially given that the function is shorter, easier to write, and easier to read (given some Clojure experience, of course) than the Commons version.

Conclusion

Clojure solves the same problems that OO solves, but it solves them in different ways. Instead of encapsulation, polymorphism, and inheritance, you have closures, namespaces, pure functions, immutable data, and multimethods. Idiomatic OO gives you a bloated type system with duplicated code hidden away behind encapsulation boundaries and little hope for thread safety. Clojure offers a radical alternative: a lean type system, a rich function library, and language-level concurrency support that is usable by mere mortals.

There is a lot more to Clojure than we have covered here: lazy and infinite sequences, destructuring, macros, software transactional memory, agents, seamless Java interop, and more. But those are topics for another day.

[This article was originally published in the May 2009 issue of NFJS, the Magazine. I will be speaking about Clojure at several upcoming NFJS events, come join the fun.]

Aug 08 2009

Comments

Why "Enterprise Tools" Are Bad for the Enterprise

In the Ruby community, we have a tendency to sneer at things that are too "enterprisey." That tendency has been explained in some uncharitable ways, but mostly by claiming that we "just don't understand the enterprise." But I think there's a different explanation. At Relevance, we've been doing some work with a large, enterprisey customer (Verizon Business). We've worked together to try to understand what enterprises can learn from smaller, more agile companies like Relevance. That work has led us to understand some of the real disadvantages of the kinds of big, pricey tools you usually find in large enterprises,and we've learned why lightweight tools like Ruby are good fits not just for small, agile teams, but for large enterprises as well.

Aug 04 2009

Comments

Pairing and Conversations with Corey Haines

Corey Haines was in our neck of the woods recently, and we were delighted to host him for three days of pairing and great conversations. We're very fond of the craftsmanship model of software development, especially as a method of training. Although we haven't gone about it as explicitly as Corey has in his journeyman tours, we all feel that we learned our skills largely through working with great programmers. (And if we had it to do over again, we'd seriously consider following Corey's example and really being journeymen for a while.) Our interview process here involves a full day on-site, pair programming with members of our team, and we often learn cool things from even that amount of cross-pollination. So we felt especially fortunate having three days with Corey.

Corey arrived with an interest in Clojure, so he spent most of his time pairing with Stuart on one of our Clojure projects. And he stayed with Muness, in his apartment right upstairs from our office. The result? Two cool video interviews:

We couldn't be happier with how these turned out. They highlight Stuart and Muness and their passions, but they show a lot about the rest of us here at Relevance as well. Thanks, Corey!

Aug 04 2009

Comments

RunCodeRun now supports private builds

We are pleased to announce that RunCodeRun is now accepting private builds. This means that you can now connect your private repositories on GitHub to RunCodeRun for your continuous integration needs. You can drop by our list of plans to see which best suits your situation.

We feel it important to note that RunCodeRun remains, as always, free for open source developers. If you are already an open source customer and want to upgrade, just sign in and head to the plans page and pick the option that works for you. You can keep all your open source projects active, now and forever, regardless of which plan you are on. And if you are just interested in getting started with an open source project? That's easy: just sign up for an open source account. You can always upgrade later if you want to add private builds.

We look forward to seeing your projects clean and green!

Jul 29 2009

Comments

Working With Multiple Ruby Versions Has Never Been This Easy

The Ruby VM market is thriving, and we increasingly find ourselves spending the morning in JRuby and the afternoon in Ruby Enterprise Edition. Or Thursday using Leopard Ruby and Friday using Ruby 1.9. Or ... well, you get the idea. So we needed a lightweight tool that eliminates the cost (in both time and frustration) of switching between Ruby versions. We wanted something drop-dead simple. Meet the Ruby Switcher.

Ruby Switcher

Hello, Ruby Switcher

The Ruby Switcher debuted earlier this year as a means for toggling between a handful of pre-installed Ruby versions. And once you bit the bullet and manually installed those VMs, and assuming you installed them in the "right" location, the Ruby Switcher offered crazy-fast switching between those few Ruby versions. But we're passionate about continuous improvement and maintaining a sharp set of tools, so we recognized that switching Ruby versions was Step 2; Step 1 (i.e., installing those Ruby versions in the first place) needed a healthy dose of automation as well. Today's Ruby Switcher makes both steps insanely simple, and it adds support for more (and newer) Ruby versions as well.

Pick a Ruby, Any Ruby

Enough talk! Let's download the Ruby Switcher and get this party started.

  cd
  curl -O -L http://github.com/relevance/etc/raw/26ae85c2f6c7d2640a3c75d619ad7ab8fc1cc570/bash/ruby_switcher.sh
  echo "source ~/ruby_switcher.sh" >> .bash_profile
  source .bash_profile

That's it. Let's quickly verify that you have your platform's core developer tools installed, and then you'll be ready to put the Ruby Switcher to work.

Prerequisites - Compilers, Libraries, and Whatnot

If you're on OS X, you'll need to download and install Xcode. (If you just want to switch between Leopard Ruby and JRuby, you can skip this step. If you want to use any other versions of Ruby, you'll need Xcode.)

If you're using Ubuntu, use apt-get to grab a few essential packages:

  sudo apt-get update
  sudo apt-get install build-essential zlib1g-dev libreadline5-dev libssl-dev

For other *nix variants ... well, clearly you enjoy figuring this stuff out.

Ruby 1.9

Yehuda Katz recently asked the Ruby community what we need in order adopt Ruby 1.9. Having an easy way to experiment with Ruby 1.9 is surely a good place to start. What could be easier than a single command?

  install_ruby_191

The installation will take a few minutes. In the meantime, why not check out Bruce Williams' slides on Ruby 1.9 from last year's Lone Star Ruby Conference — Ruby 1.9: What's New and Why it Matters?

  ...
  checking build system type... i686-pc-linux-gnu
  checking host system type... i686-pc-linux-gnu
  checking target system type... i686-pc-linux-gnu
  checking for gcc... gcc
  ...
  installing rdoc
  Using ruby 1.9.1p129 (2009-05-12 revision 23412) [i686-linux]
  Successfully installed rake-0.8.7
  1 gem installed

By the time you're done reading through the Ruby 1.9 highlights, you'll be ready to take it for a spin. Type ruby -v to verify that you're rockin' with 1.9.1.

  ruby 1.9.1p129 (2009-05-12 revision 23412) [i686-linux]

JRuby

Installing and using JRuby is just as easy.

First, be sure that you have the JDK installed. If you're using OS X, you're all set. If you're on Ubuntu, you can install it with apt-get. (For other Linux distros, you can ask your native package manager for the JDK bits.)

  sudo apt-get update
  sudo apt-get install sun-java6-jre sun-java6-jdk

With the JDK in place, installing JRuby is a cinch.

  install_jruby

Verify the results with ruby -v.

  jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d) (Java HotSpot(TM) Client VM 1.6.0_10) [i386-java]

Wait a minute: shouldn't we have typed jruby -v instead of ruby -v? Sure you can do that if you really want to. But we're not big on having to remember to type jirb instead of irb, or jgem instead of just gem, etc. Once we tell the shell to use JRuby, we want the normal Ruby commands to just work. The Ruby Switcher ensures that they do.

Ruby.*

Looking for yet another Ruby version? See what else the Ruby Switcher has to offer:

  • install_ree_186
  • install_ruby_186
  • install_ruby_187
  • install_jruby_120

And if you find yourself wanting some other Ruby variant, be sure to check out the Ruby Switcher's internals; you can likely adapt one of the existing installation functions to meet your exact need.

Willy-nilly Switching

Once you have the desired Ruby versions installed, switching between them couldn't be easier. Each of the install commands comes with a corresponding use command to instruct your shell to switch to the specified Ruby version:

Install It Use It
install_ruby_191 use_ruby_191
install_ruby_186 use_ruby_186
install_ruby_187 use_ruby_187
install_jruby use_jruby
install_jruby_120 use_jruby_120
install_ree_186 use_ree_186
N/A [1] use_leopard_ruby

And these commands are shell-specific! So while one terminal window is using Ruby 1.8.7 to run your front-end Rails app, you can have another terminal using JRuby to run your back-end messaging code.

Ruby 1.9, the Community, and You

Want to see whether your app runs on Ruby 1.9? Flip your shell to use_ruby_191 and try it out. It's that kind of experimentation that will allow the Ruby community to make the migration to Ruby 1.9.

See a problem with a certain gem on Ruby 1.9? Let the gem author know about it and chime in at isitruby19.com. Better yet, write a test that proves the bug and pass that test along to the author. Better still, fix the issue and submit a patch. Any one of these steps moves us forward, and all the while you've still got your default Ruby installation standing safely by for your day job.

When switching between Ruby versions is this seamless, there's no reason not to experiment.

Notes

[1] Ruby is installed by default on OS X Leopard, so there's no need for an install_leopard_ruby command.

Image courtesy of bdu (flickr.com/bdu). [Creative Commons License]

Jul 28 2009

Comments

rcov Bug Reporting - Knowing Is Half the Battle

As you've probably gathered by now, we're passionate about testing. Over the course of the past year, we've implemented various improvements to rcov — the Ruby code coverage analysis tool — to help satisfy our testing appetite.

To date, we've squashed numerous bugs, simplified the report-generation logic (making way for better template support in the future), and achieved experimental support for Ruby 1.9. While working out bugs reported by developers using our fork of rcov (relevance-rcov), one thing has become painfully obvious to us: the bug reports lack the context we need to properly troubleshoot the issues. Fortunately, rcov is bundled with a handy tool that can help mitigate this problem, and we'd like to take a moment to share the details.

Let's say you're having an issue with rcov and you want to report a bug. First, simply locate the test that produces the bug. Then, use rcov's report-cov-bug flag to produce the bug report. From your console run:

  rcov --report-cov-bug='Class#method' path/to/testfile

As a practical example, let's say that we have a test for a Rails ActiveRecord class called Project and an offending method called #archived?. This class has Micronaut-based tests located in examples/models/project_example.rb. In order to produce an rcov bug report you would run:

  rcov --rails --report-cov-bug='Project#archived?' examples/models/project_example.rb

This will produce a nicely-formatted bug report that tells us what version of Ruby you are running as well as your version of rcov, the arguments you passed in, and the method in question. Along with this information, it will provide additional details about how the code was analyzed. The end result will look something like the following:

  Bug report generated on Wed Jul 22 13:17:35 -0400 2009

  Ruby version:              1.8.6 (2008-08-11)
  Platform:                  universal-darwin9.0
  rcov version:              0.8.3.4
  rcovrt loaded?             true
  using RubyGems?            true
  Command-line arguments:    ["--rails", "--report-cov-bug=Project#archived?", "examples/models/project_example.rb"]
  Coverage analysis bug in:  Project#archived?

  Line(s) 46 should be green.
        
  Raw coverage information (feel free to remove useless data, but please leave
  some context around the faulty lines):

        3:   45:  def archived?
        2:   46:    archived_at
        2:   47:  end
        0:   48:

Once you have that information, you're ready to log the issue at github.com/relevance/rcov/issues.

Known Issues with report-cov-bug

As helpful as the report-cov-bug tool is, it does fall down in a few places:

  • ActiveRecord "declarative" methods (e.g., named_scope, default_scope) report the actual ActiveRecord code, not the model code.
  • The C version of #callsite does not accurately report executed methods for Ruby 1.9.

Supported Environments

We use rcov on Linux and OS X for all our Ruby 1.8.x projects. If you run into an issue using rcov in this environment, please let us know.

What about other runtimes and platforms?

  • For JRuby projects, be sure to check out jruby-rcov. (relevance-rcov does not currently support JRuby.)
  • The Ruby 1.9 support is not ready for prime time. We consider it an experiment at this point.
  • We are not Windows users day-to-day, so Windows is not currently supported. However, you're welcome to fork the repository if you have the urge to scratch that itch.

You Can Help Build a Better rcov

Providing the report-cov-bug output with your bug report gives us a much better chance of tracking down the root cause of the issue. Take relevance-rcov for a spin on your project, and if you see any hiccups, let us know.

Jul 27 2009

Comments

In Boston Aug 15: Triadic Programming

I will be premiering the Triadic Programming keynote at Developer Day Boston on August 15. The talk pulls together ideas I have been working out for over a decade, and proposes a new way to think about the abstractions that programmers use.

Of course, the best part of any technical event is happy hour, so I will be sticking around for the evening to meet folks, along with our excellent co-sponsors from Viget Labs and thoughtbot.

Oh, and there are lots of other interesting talks on the agenda, including one by some guy who apparently knows a bit about JavaScript. Hope to see you there!

Popular Tags