Customizing Generators in Rails 3

January 13, 2010

As you probably already know, Rails 3 is just around the corner. There are some pretty nice features being added and one of them is the ability to customize the way the generators work. I personally prefer Haml over ERB, RSpec over Test::Unit and Factory Girl over Fixtures. So let's see how we can configure a Rails 3 app to do that.

First, follow Yehuda's instructions on how to create a Rails 3 app. Next, you have to tell Rails that you want to use Haml, RSpec and Factory Girl. First, add this somewhere in the Gemfile:

gem "haml"

only :test do
  gem "rspec"
  gem "rspec-rails"
  gem "factory_girl"
end

Then, re-run the bundler and initialize the Haml plugin:

$ gem bundle
$ bin/haml --rails .

Finally, you have to install the custom generators for the frameworks that you want to use. I found a repo on github that already had RSpec, so I forked it and added Haml and Factory Girl. Clone the repo into the lib/generators directory of your app:

$ git clone git://github.com/pjb3/rails3-generators.git lib/generators

Now, in the config/application.rb file in your app, near the bottom there is a section related to config.generators. Put this in that section:

config.generators do |g|
  g.template_engine :haml
  g.test_framework :rspec, :fixture => true, :views => false
  g.fixture_replacement :factory_girl, :dir => "spec/factories"
end

Here is where we reap the benefits of the modularity in Rails 3. What this says is that we want to use Haml as the template enging, we want to use RSpec as the test framework and we want to generate fixtures with our generated specs, but we don't want to generate view specs and that instead of fixtures, we actually want to use factory girl and we want the factories to be put into spec/factories. Whew! So does this all work?

$ script/generate rspec:install
$ script/generate scaffold person name:string dob:date age:integer male:boolean bio:text

At this point you should see that Rails has generated what we want, which is scaffolding that uses Haml for the views, RSpec for the tests and Factory Girl for the fixtures. Run the migrations, start the server and open the browser http://localhost:3000/people to see your scaffolding in action.

Now if you try to actually run rake spec, you'll get an error, at least I do. I'm not sure that RSpec 1.X is going to ever work with Rails 3. I think the intent is for RSpec 2 to be compatible with Rails 3, so keep an eye out for that.

Posted in Technology | Tags FactoryGirl, RSpec, Rails, HAML | 2 Comments

Story Driven Development with Rails - Part I: Up and Running

September 16, 2008

Earlier this year at the Gotham Ruby Conference, Brian Helmkamp gave a talk on Story Driven Development. It's a fantastic talk, I suggest that everyone view it. In it he explains how to use RSpec Users Stories combined with Webrat for full-stack executable scenario testing. He also covers what value executable scenarios provide and where executable scenarios fit into your overall web application testing strategy. This article provides you with a more detailed how-to of the steps required to get up and running with Story Driven Development with Rails.

In this article I don't go to much into the details of how story runner works, so I would say another prerequisite for this article is Geoffrey Grosenbach's RSpec User Stories available on PeepCode.

So let's say we are building a rails version of this blog. First steps are the basic rails setup:

$ rails blog
$ cd blog
$ script/plugin install git://github.com/dchelimsky/rspec.git -r 1.1.4
$ script/plugin install git://github.com/dchelimsky/rspec-rails.git -r 1.1.4
$ script/generate rspec      
$ script/plugin install git://github.com/ratnikov/fixture_replacement2.git 

That gives us an rspecified Rails app, which has a stories directory, all ready for us to put our stories in. Fixture Replacement is an implementation of the factory pattern for creating test data, which is an alternative to using fixtures data in yaml files. So let's create the first story in stories/view_articles_story.txt:

Story: View Articles

As a reader of the blog
I want to view articles
So that I can read what you have to say

  Scenario: display most recently published articles

    Given 5 articles have been published
    When I visit the homepage
    Then I should see the articles

A couple of things to point out. First, the naming convention I'm going with is to put the plain text story in <story_name>_story.txt and the story runner file in <story_name>_story.rb. Next I'd like to talk a little bit about this story. I choose to write this story first because it is the most important feature in the application. Generally I like to try to follow this rule. When writing a stories, always write a story for the next most important feature.

So in order to use this story, we need a runner. So let's put this into stories/view_articles_story.rb:

require File.dirname(__FILE__) + "/helper"

run_story :view_articles

This is about concise as it gets for this file. The magic happens in stories/helper.rb. Add this to the bottom of that file:

def run_story(story_name, options={})
  with_steps_for(story_name) do
    run(
      File.join(File.dirname(__FILE__), "#{story_name}_story.txt"), 
      { :type => RailsStory }.merge(options)
    )
  end
end

What this does is allow you to call run_story to run a story. It assumes by default that you will want to use the steps that are defined in the same file with the same name as the story. We'll probably enhance run_story in the future to do more stuff, but this is all we need for now. Now you can run either stories/all.rb or stories/view_articles_story.rb and you should get:

Running 1 scenarios

Story: View Articles

  As a reader of the blog
  I want to view articles
  So that I can read what you have to say

  Scenario: Homepage

    Given 5 articles have been published (PENDING)

    When I visit the homepage (PENDING)

    Then I should see the articles (PENDING)

1 scenarios: 0 succeeded, 0 failed, 1 pending

Pending Steps:
1) View Articles (Homepage): 5 articles have been published
2) View Articles (Homepage): I visit the homepage
3) View Articles (Homepage): I should see all 5 articles

So we now have yellow (a.k.a pending) scenarios, so step 1 is done. The next step is to write step matchers, which should make our scenario fail. So we add this to stories/view_articles_story.rb:

steps_for(:view_articles) do
  Given "$count articles have been published" do |count|
    @count = count.to_i
    @count.times {|n| create_article(:title => "Article ##{n}") }
  end
  When "I visit $path" do |path|
    get path
  end
  Then "I should see the articles" do
    @count.times do |n|
      response.should have_tag("h2", "Article ##{n}")
    end
  end
end

Make sure to add that before the run_story :view_articles line. Now you should have red (a.k.a failing) scenarios, that look something like this:

FAILURES:
    1) View Articles (Homepage) FAILED
    NoMethodError: undefined method `create_article' for   
    #<ActionController::Integration::Session:0x208bfe0>

Make sure they are failing for the right reason. For example, when I first wrote this, I forgot that all the of variables are captured as strings and I left off the call to .to_i on count. I get failing specs, but not because I haven't implemented the code, because my steps are wrong. Remember, tests can have bugs too.

Let's take a look at each of these steps. Inside the given step, we are calling create_article. Now as you noticed from the failure message, there is no create_article method yet. Fixture Replacement will take care of defining that for us once we have the articles model created and example_data.rb created. In the when step, we are capturing the path the user is trying to access, and then using the same rspec controller helper method we would use if this were a controller spec. get /some_path goes through the routing to call our controller with an HTTP get. Finally, in the then step, we just check that the HTML in our response has an H2 element with the title of the article in it, for each of the articles we created in the given step.

So now on to step 3, which is to write the code to make it go green. To make this easy, we'll use the rspec scaffold generator:

$ script/generate rspec_scaffold Article title:string body:text
$ rake db:migrate
$ rake db:test:prepare

So if we run stories/view_articles_story.rb again, we get the same error. That's because we still haven't told Fixture Replacement about our article model. So create the file db/example_data.rb and put this in it:

module FixtureReplacement
  attributes_for :article do |a|
    a.title = "First Post!"
    a.body = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit..."
  end
end

This defines the minimum set of data required to create a valid article. You can override any value by simply passing it in the hash, as we are doing in the given step. Fixture Replacement gives you two methods for each model, which are in this case, create_article and new_article, which do pretty much what you would expect. You can read more about Fixture Replacements in the README.

Last but not least, we have to include FixtureReplacement in our RSpec Story Runner, which is as simple as adding the line include FixtureReplacement somewhere near the top of stories/helper.rb, after all the other require statements.

So now if we run our story, we get something like:

1) View Articles (Homepage) FAILED
    Spec::Expectations::ExpectationNotMetError: 
    Expected at least 1 element matching "h2", found 0.
    <false> is not true.

Which is still failing, but it's progress. Obviously the problem now is that our view isn't outputting H2 elements for the article titles. That's an easy fix which I'll leave as an exercise to the reader (hint: modify the default route, edit the articles index ERB template).

One might argue that checking for H2s is checking the implementation details, which could be fragile if our designer decides to use DIVs instead, for example. That decision is up to you, personally I like to produce the correct semantic markup, have my tests validate that and apply CSS from there. If you'd rather your tests be less coupled to the DOM structure, you could just change your expectation in the then step to response.should have_text("Article ##{n}").

Hopefully that gets you up and running with Fixture Replacement and RSpec User Stories. Tune in for Part II of the series, where we cover more complex interactions with Webrat.

Posted in Technology | Tags RSpec, Ruby, StoryRunner, Rails, FixtureReplacement | 10 Comments

Displaying error messages in RSpec failures

April 23, 2008

The suggested way of checking if an ActiveRecord model has errors with RSpec is:

@something.errors.should be_empty  

When that fails, you get a report that says:

expected empty? to return true, got false

That's not very helpful because it doesn't tell you what the errors are. If you do this:

@something.errors.full_messages.should == []      

You get a more informative failure message:

expected: [],
got: ["Whatever can't be blank"] (using ==)

Posted in Technology | Tags RSpec, Ruby, Rails | 6 Comments

The Edge of Innovation

April 11, 2008

If you are a programmer that deals with web applications and you keep up with the latest trends, then there is no doubt you will at least have heard of Ruby on Rails. You might be at the level where you have read about Ruby on Rails and played with it a bit, but you really haven't immersed yourself the Ruby/Rails way. Maybe you know how to build web applications with Java, .Net or PHP, and you think "Rails is nice, but there's nothing Rails can do that you can't do in X".

If you are in this camp, it may be because you aren't willing to adapt your ways to Rails. To really understand the benefits of Rails, you have to not only learn Rails, but learn the best practices followed by good Rails programmers, like Skinny Controller, Fat Model, REST and Behavior Driven Development. You don't see the true benefit of Ruby until you start to fully embrace concepts like these. The only way to learn concepts like these is to read blogs, listen to podcasts, talk to other Ruby programmers at work, user groups and conferences and most importantly, you have to actually write code that reflects what you have learned. You must put aside your preconceived notions about what is right and what is wrong and surrender to the flow. You must unlearn what you have learned.

So if you haven't experienced this transformation first hand yet, and someone asks you "What is the biggest advantage to Ruby on Rails", I would be willing to bet your answer would be productivity. This is the way I think many people involved with technology, who don't fully grasp the Rails Way, perceive Rails. They believe, with some cynicism, that because of the dynamic nature of Rails, you can develop applications faster. They'll also probably say the downside is that Rails can't scale.

But anyway, I'm hear to say that productivity is the most over-rated benefit of Rails. The real advantage to Rails is that it is written in Ruby, which is a very powerful language that will change the way you think about programming. It's funny, I've thought to myself a number of times about how interesting it is that Ruby fundamentally changes the way you think about programming and that "Thinking in Ruby" would probably be a great book. But ironically, Bruce Eckel, the author of the "Thinking in..." line of programming books, seems to be happy with Python and not willing to give Ruby a chance. Who knows, that article is a few years old now, so maybe he's changed his attitude towards Ruby since then. I know mine has.

It's hard to quantify the advantage that "Thinking in Ruby" brings. The simplest way I can state it is that you will look at problems differently and come up with better solutions, solutions you may not have thought of if you were programming in other languages. The way I support this claim is by looking at some of the web application development innovations that have come out of the Ruby community.

The first is Rails itself. Rails has been copied, ported, attempted to be ported or talked about being ported to almost every other mainstream language you could think of, including Groovy, PHP, JavaScript, Perl, Java and .Net. This phenomenon is unique to Rails, I can't think of any other web application framework that can say that. If not the whole framework itself, parts of it such as convention over configuration, migrations and embracing REST have influenced the way web application development is done in almost every language.

Another example is HAML. HAML is a truly new and different take of the problem of generating HTML from a combination of dynamic code and HTML. It is a new idea and it has been ported to PHP, Python, and .Net. Whenever you have a framework or library that is being ported to other languages, it shows that the framework being ported contains new and good ideas about programming. In other words, it is a contribution to the paradigm of web development and a clear sign that the original language that the framework was implemented is at the edge of innovation.

Another example is Behavior Driven Development. This example is even more interesting because the idea originally started in Java with the JBehave framework. Even though the idea for behavior driven development started with Java, the idea didn't really take off until it was implemented in RSpec. They are fairly similar in terms of syntax. Here's an example from the JBehave website:

public class CheeseBehaviour extends UsingJMock {
    public void shouldRequireTheUseOfMocks() {
        // given
        Mock cheese = new Mock(Cheese.class);
        Sheep sheep = new Sheep((Cheese)cheese.proxy());

        //expect
        cheese.expects(once()).method("squelch").withAnyArguments();

        // when
        sheep.eatCheese();

        // then
        verify();
    }
}

and here it is converted to RSpec:

describe Sheep do
  before do
    @cheese = mock(Cheese)
    @sheep = mock(Sheep, :cheese => @cheese)
  end
  it "should squelch when it eats cheese" do
    @cheese.should_receive(:squelch)
    @sheep.eat_cheese
  end
end

For whatever reason, JBehave really never took hold in the Java community, but RSpec has in the Ruby community. RSpec has been ported to .Net, PHP and Groovy. All of those projects describe their code as a port of RSpec, not JBehave. Again it is Ruby influencing the wider web application development community.

Post World War II, the center of the art world was New York City and it was there that the modern art movemement was born. New York was where innovation in the art world was happening. In that time period if you wanted to be a serious artist, you had to go to New York to experience the movement first hand. Today, I believe the Ruby community is leading the way in innovative techniques for web application development. There is certainly innovation happening in other languages like Python, Smalltalk and Erlang as well, but I don't think any one other language/community is doing as much as Ruby. As far as I can tell, languages like Java, .Net and PHP are doing nothing to innovate web application development. They are simply lagging behind, playing catch up and trying to figure out how implement new features pioneered in the Ruby community and others as closely as possible, given the limitations of the language. So if you are a web developer, I suggest you ask yourself this question. Are the languages and frameworks you are working with leading others to come up with new ideas? Are the languages and frameworks that you are working helping you come up with new ideas? If not, embrace Ruby and someday you will discover an elegant solution to a problem, one that you may not have without Ruby.

A language that doesn't affect the way you think about programming is not worth knowing. -- Alan Perlis

Posted in Technology | Tags Javascript, RSpec, .Net, Python, Ruby, Java, Rails, HAML, PHP | 0 Comments