Rails appears pretty strict about separation of the abstraction layers that make up its notion of a web application: models, view and controllers. If you were to suggest calling a presentation method, such as url_for, in your model, the stoic Rails advocate will have an allergic reaction. However, Rails thinks nothing of rendering a model directly as a view, such as:

format.json { render :json => @products }

Now, one might argue that this is controller code and the controller is allowed to interpret the model as a view. The controller’s job is to mediate this interaction. However, I feel that it is a dangerous shortcut, made even more so by how hard to seems to be to override. Perhaps the json implementation is simply incomplete.

In xml, this strange controller pattern is easily corrected by providing an xml view. The xml builder syntax is particularly readable, and it is easy to design your XML API effectively.

I haven’t found an equivalent for json. I tried to use a JSON API today to no avail. My model included image data which breaks when auto-rendered in JSON. What I really wanted was to include a URL instead of the image data, which I implement neatly in my xml.builder view:

xml.instruct!
xml.products("type"=>"array") do
  @products.each do |product|
    xml.product do
      xml.sku product.sku
      xml.name product.name
      xml.brand product.brand
      xml.img_url url_for(:controller => :products, :action => :show, :format=>:png, :id => product.id, :only_path => false)
    end
  end
end

The problem is that I want a similar view in JSON. The to_json API leads me to put this logic in my model (gasp!). In fact, the ActiveRecord::Serialization docs give an example of providing a method to generate JSON instead of a literal attribute. The example is of a “permalink” which seem suspiciously like something that belongs is the view layer.

  konata.to_json(:methods => :permalink)
  # => {"id": 1, "name": "Konata Izumi", "age": 16,
        "created_at": "2006/08/01", "awesome": true,
        "permalink": "1-konata-izumi"}

Today’s solution was to go back to using my comfortable old XML API, but I would prefer to consume JSON from the other side. I wonder if anyone is working on a JSON builder or if there is some clear solution that I haven’t yet stumbled upon.

In preparation for teaching Ruby in a class with test first teaching. I decided to evaluate a few test frameworks. I thought initially to use Test::Unit, since it seemed easy to understand and ships with Rails. Wolfram Arnold argued that Test::Unit would burden the new folks with legacy. Alex Chaffee also advocated RSpec, but other friends from the Twittervese had good things to say about shoulda. Some folks declared it to be simply a matter of taste.

Even so, I wanted to make an informed decision and refine my palette for Ruby tools, so I wrote a simple exercise in each of Test::Unit, Shoulda and RSpec.

Test::Unit

require 'test/unit'
require 'pig_latin'
 
class PigLatinTest < Test::Unit::TestCase
    include PigLatinTranslator
 
    def test_simple_word
        s = translate("nix")
        assert_equal("ixnay", s)
    end
 
    def test_word_beginning_with_vowel
        s = translate("apple")
        assert_equal("appleay", s)
    end
 
    def test_two_consonant_word
        s = translate("stupid")
        assert_equal("upidstay", s)
    end
end

With the above code saved as “test_pig_latin.rb” you run it by simply executing it with Ruby.

$ ruby test_pig_latin.rb
Loaded suite test_pig_latin
Started
FFF
Finished in 0.01091 seconds.

  1) Failure:
test_simple_word(PigLatinTest) [test_pig_latin.rb:9]:
 expected but was
.

  2) Failure:
test_two_consonant_word(PigLatinTest) [test_pig_latin.rb:19]:
 expected but was
.

  3) Failure:
test_word_beginning_with_vowel(PigLatinTest) [test_pig_latin.rb:14]:
 expected but was
.

3 tests, 3 assertions, 3 failures, 0 errors

Shoulda

Notice in the code below that Shoulda is simply and extension to Test::Unit. The PigLatinTest also subclasses Test::Unit::TestCase, just as the example above; however, the code inside the test case looks substantially different (and more readable in my opinion). You can actually mix Shoulda tests (below) with regular TestCase test methods (above) in the same TestCase. This is an advantage to Shoulda over RSpec if you have a codebase that already has lots of unit tests; however, I have also used RSpec and Test::Unit in the same project (you just have to remember to ‘rake test’ and ‘rake spec’).

require 'rubygems'
require 'shoulda'
require 'pig_latin'

class PigLatinTest < Test::Unit::TestCase
  include PigLatinTranslator

  context "#translate" do

    should "translate a simple word: nix" do
      s = translate("nix")
      assert_equal("ixnay", s)
    end

    should "translate a word beginning with a vowel: apple" do
      s = translate("apple")
      assert_equal("appleay", s)
    end

    should "translate a two consonent word: stupid" do
      s = translate("stupid")
      assert_equal("upidstay", s)
    end

  end
end

With the code above saved as “test_shoulda_pig_latin.rb” you use the same process as above by just executing the file with ruby.

$ ruby test_shoulda_pig_latin.rb
Loaded suite test_shoulda_pig_latin
Started
FFF
Finished in 0.008268 seconds.

 1) Failure:
test: #translate should translate a simple word. (PigLatinTest)
 [test_shoulda_pig_latin.rb:12:in `__bind_1251676444_52936'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a simple word. ']:
<"ixnay"> expected but was
<"translation">.

 2) Failure:
test: #translate should translate a two consonent word. (PigLatinTest)
 [test_shoulda_pig_latin.rb:22:in `__bind_1251676444_58860'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a two consonent word. ']:
<"upidstay"> expected but was
<"translation">.

 3) Failure:
test: #translate should translate a word beginning with a vowel. (PigLatinTest)
 [test_shoulda_pig_latin.rb:17:in `__bind_1251676444_59935'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a word beginning with a vowel. ']:
<"appleay"> expected but was
<"translation">.

3 tests, 3 assertions, 3 failures, 0 errors

RSpec

require "pig_latin"

describe "#translate" do
  include PigLatinTranslator

  it "should translate a simple word" do
    s = translate("nix")
    s.should == "ixnay"
  end

  it "should translate a word beginning with a vowel" do
    pending
    s = translate("apple")
    s.should == "appleay"
  end

  it "should translate a two consonent word: stupid" do
    pending
    s = translate("stupid")
    s.should == "upidstay"
  end

end

The code above is saved in a file called “pig_latin_spec.rb” and run it using the ‘spec’ command. You will need to have installed the rspec gem (sudo gem install rspec).

$ spec pig_latin_spec.rb
F**

Pending:

#translate should translate a word beginning with a vowel (TODO)
./pig_latin_spec.rb:11

#translate should translate a two consonent word: stupid (TODO)
./pig_latin_spec.rb:17

1)
'#translate should translate a simple word' FAILED
expected: "ixnay",
     got: "translation" (using ==)
./pig_latin_spec.rb:8:

Finished in 0.035728 seconds

3 examples, 1 failure, 2 pending

Conclusion

I like RSpec best since I find the output to be most readable. I love the pending keyword, which allows me to set up the tests as an exercise for the class with only one test failing. I find it helps focus on exactly one test and one failure. I considered going with Shoulda because the tests are just as readable as RSpec, even if the output takes some learning to read, because of my initial thought that Test::Unit held less magic. However, on closer inspection, I realized that Test::Unit has one significant magical incantation: you merely declare a class and when that class is defined, it runs the test. This seemed not the kind of topic I would want to teach in an intro class. Even some experienced programmers might struggle with understanding the mechanism that allows such a construct to function. I concluded that all of the test frameworks require serious magic, and picked RSpec since I found it to be most usable for test writing and analysis of the output.

Caveat: this exercise was for pure Ruby. In Rails, I wonder if Shoulda tests would be more concise, making them easier to write and read and, therefore, making it worth the steeper learning curve on reading the output.

In preparing to teach a new class, Ruby on Rails Fundamentals, I felt strongly that I wanted to teach test-driven development (TDD).  Initially my motivation was that I found it hard to learn TDD when I learned Rails.  I found little instructional material on the web and the variety of test frameworks was a bit overwhelming.  As an experienced programmer, I understood the methodology (although I hadn’t practiced it much), but couldn’t find good resources to teach myself the basic syntax.  I found cucumber quite approachable, but struggled with TestUnit and RSpec (which I eventually learned well through pair programming).  I figured that if people didn’t learn TDD in an introductory class, how the heck were they to learn it at all?  Also, the last thing I want is for graduates of my class to have experience building web applications in Ruby on Rails that have no tests.

There were negative reactions from some folks via email and twitter, but I had just as many people encouraging the teaching of TDD, so I decided to move forward with my plan.  Wolfram Arnold is a big proponent of throwing the newbies into the status quo.  We should teach them the real thing, not yesterday’s news, and certainly not teach them practices that we would frown upon.  I reflected on my own TDD experience and realized that one of the benefits of TDD and one of the things that makes it hard is that I really need to understand what my code is doing to test it.  That might sound strange, but it is surprisingly easy to get an intended high-level effect you want without really understanding exactly what your code is doing, especially with Rails.  If you force yourself to write a test, you are creating a detailed contract with yourself.  It is a crystallization of your design that requires an understanding of the code.  I thought that would be really good for the class.  We could write tests that cover some important details that it is easy to gloss over as a newbie.  In this way, TDD would start as a teaching/learning tool, rather than the design tool that it usually is in a typical development process.

Today I met with another TDD teaching proponent, Alex Chafee.  He had twittered that he always teaches testing first, and I was intrigued. I joined his regular Sunday study group as a TA, where I had fun teaching about nested routes, debugging collection_select, and spreading the word about nested attributes.  After the class, a few students stayed late and joined me as I learned from Alex about his approach to teaching, which he calls test-driven teaching (TDT).  He did this intially in teaching java and more recently teaching Ruby.  For homework assignments he would give student tests and then they would write the code to make them pass.  This is one of those ideas that I hadn’t thought of, but as soon as he told me about it, the idea seemed an obvious way to teach.  What beter way to specify an assignment that to provide a test?

What I’d ike to do for the class is to start with TDT and then shift to TDD once the students are familiar enough with the Ruby syntax and the Rails object model.  It is perhaps aggressive for 6 2-hour classes, but with homework assignments between classes and students who are already programmers, it seems like it could fit into the time frame.

My first challenge is to figure out what are the essential Ruby language constructs that people need to know for writing Rails applications, what would be good exercises for learning those skills, and then write the tests that would require that code to be written.  It feels like a little bit ‘o Ruby jeopardy.  Alex has promised to meet with me again to brainstorm and I’ll publish what we come up with, but if you are reading this and you write Rails applications, I would love your feedback on what are the essential Ruby skills for a Rails engineer (plus any Ruby, Rails or TDD teaching tips would be welcome).