RailsConf – Don’t Mock Yourself Out.

Posted by Brian in News, Rails (May 5th, 2009)

David Chelimsky gave a great presentation on mocking and stubbing. I liked the fact that he talked about a lot of the fears that people have when they do rely on mocks and stubs. It was also nice to see someone clearly state that one of the problems we have in the Rails community when it comes to testing frameworks (and most other libraries) is that people tend to promote their own projects while trashing other libraries. He challenged an audience member to create a site where we could write up comparisons.

Highlights of the talk:

  • Stubs vs. mocks
  • “Ravioli” code where everything is clumped up in nice separate concerns
  • “Calzone” code, like Rails, which is harder to test in isolation.
  • Stubble, a wonderful-sounding library that can completely stub out an ActiveRecord object, which will be usable in every testing framework.

RailsConfigModel updated

Posted by Brian in News, Projects, Rails (April 22nd, 2009)

The Rails Config Model gem makes it extremely easy to create a “settings” table in your application. It creates a configuration controller and model that can be used to quickly create configuration table for your system so you can store system-wide variables that you’d like the site administrator to be able to set.

You can see instructions on usage or contribute to the project.

LazyDeveloper update

Posted by Brian in News, Projects, Rails (April 22nd, 2009)

For those unfamiliar, LazyDeveloper is a Rails plugin that provides tasks to simplify some cumbersome development tasks. I find it to be an indispensable tool. The latest version (1.1.4) fixes a couple of bugs related to running tests, and introduces a couple of new features which you can read about at the project page.

This is the last release of the plugin. I’ve decided to move this to a gem instead.

If you’re interested in contributing, feel free to fork the project and send pull requests!

Reverse Proxy Fix for Rails updated to support Rails 2.3

Posted by Brian in News, Products, Rails (March 16th, 2009)

Hot on the heels of the Rails 2.3 release is the new version of Reverse Proxy Fix. This new version only adds support for Rails 2.3 applications while continuing to support all previous versions.

Learn more about the Reverse Proxy Fix plugin.

Using Git to fork and contribute to a Rubyforge project

Posted by Brian in Howto, News, Rails, tips (January 13th, 2009)

If you’ve never comtributed to an open-source project before, there’s no better time to start. This article will walk you through forking an open-source project on RubyForge and making changes to it using Git. At the end, you’ll create a unified diff that can be sent back to the original author.

My current project FeelMySkills helps creative professionals promote themselves by creating an online portfolio that shows what they can do rather than who they know. I wanted the site to let a user export their profile page as a PDF, and so I found the amazing HTMLDoc gem which takes simple HTML pages and converts them to PDF documents.

While developing the site, I discovered that there is a bug in PDF::HTMLDoc that pops up when you embed images into the PDF. It turns out that it has to do with extra whitespace getting into the content which causes HTMLDoc to choke. The fix is really simple, and after I fixed it I found that there is already a patch for this posted to Rubyforge but it hasn’t been applied. I assume that the reason it’s not applied is that there were no tests supplied with the patch.

Let’s get the code, write the test, and submit the patch!

GITing the code

The HTMLDoc project (http://htmldoc.rubyforge.org) has a Subversion repository that we can use as our master branch. Now, if you’ve never used Git before, don’t worry about it because we’re only going to use it here as a really easy way to create patches.

Installing Git

Mac users with XCode and Macports installed can do it with

sudo port install git-core +svn

Windows users can install Msysgit.

Linux users should install Git using their package manager or from source.

Forking the code from Subversion

The git svn command lets you pull and push to a Subversion repository and is perfect for watching projects that don’t use Github yet.

Visit the project page at http://rubyforge.org/projects/htmldoc/ and click the link on the bottom for SCM Repository. That page lists the repo as http://htmldoc.rubyforge.org/svn.

Grab the trunk for HTMLDoc from RubyForge.

git svn clone http://htmldoc.rubyforge.org/svn/trunk htmldoc

Test before you start

If you want to start things off on the wrong foot, just start hacking away at the code. A much better approach is to see what tests are broken before you start working. Good Ruby projects should have a test suite that completely passes.

In the htmldoc folder, run rake which will run the test suite for this app.
Unfortunately the test suite shows an error:

Loaded suite -e
Started
..E........
Finished in 3.500649 seconds.

  1) Error:
test_get_command_pages(BasicTest):
NoMethodError: undefined method `path' for nil:NilClass
    ./test/basic_test.rb:91:in `test_get_command_pages'

11 tests, 361 assertions, 0 failures, 1 errors

However, on further inspection, the error is because of a path issue. The tests pass when you run them individually:

cd test
ruby basic_test.rb && ruby generation_test.rb
cd ..

See? Everything works!

Loaded suite basic_test
Started
......
Finished in 0.010653 seconds.

6 tests, 324 assertions, 0 failures, 0 errors
Loaded suite generation_test
Started
.....
Finished in 3.403335 seconds.

5 tests, 38 assertions, 0 failures, 0 errors

Creating a new branch for your changes

We’ll want to keep our work in a new branch. This makes it easy for us to create the patch later, as we can make Git give us the difference between the master branch, which we forked from Rubyforge, with our new branch which will contain our fixes.

$ git checkout -b fix_images

Writing a new test

Before you start hacking away on a new feature, you should write a test to prove that things are really broken. In this case, a PDF that contains a reference to an image causes things to break. A test that tries to put an image reference into the PDF data should break. Add this test to test/generation_test.rb

  def test_generation_results_with_image
    pdf = PDF::HTMLDoc.new
    pdf.set_option :webpage, true
    pdf.set_option :toc, false
    pdf << "

Random title

something you have to follow either.

Run this test

cd test
ruby generation_test.rb -n test_generation_results_with_image
cd ..

and you'll see that things don't work as expected:

Loaded suite generation_test
Started
F
Finished in 0.349939 seconds.

  1) Failure:
test_generation_results_with_image(GenerationTest) [generation_test.rb:43]:

expected to be kind_of?
 but was
.

So we can reproduce the bug, and now we just have to fix the problem.

A simple fix (this time)

The reason for the error is because HTMLDoc hates extra whitespace created by the inclusion of the image. Open up lib/htmldoc.rb and change line 186 from

          case line

to

          case line.strip

Save the file. Now run the entire test suite again to make sure that nothing else broke.

Loaded suite basic_test
Started
......
Finished in 0.007855 seconds.

6 tests, 324 assertions, 0 failures, 0 errors
Loaded suite generation_test
Started
......
Finished in 3.434714 seconds.

6 tests, 43 assertions, 0 failures, 0 errors

Hurray! We fixed it! Now we just have to make a patch!

Commit your changes!

You should commit your changes to your branch so you don't lose them.

  git commit -a -m "Fixed problem when embedding images in PDFs"

Making a patch

If you're quick, you can probably just create the patch right now, but let's assume the project is a fast-moving one, like Rails. Youl want to pull down the latest version of the project and fix any conflicts.

git checkout master
git svn rebase
git checkout fix_images
git rebase master

There won't be any collisions you have to fix now, so you can just make the patch file.

git format-patch master --stdout > htmldoc_fix_images.diff

This creates the file htmldoc_fix_images.diff which is a unified diff containing both the fix and the test. You can now send the patch file to the maintainer who will happily apply it because it has tests!

Wrapping up

Working with open-source projects gets easier every day, and you can really take ownership of the tools you use if you get involved. Hopefully this article gets you started on the path to contributing to projects. Good luck, and leave comments if something needs more clarification!

Rails 2.2, MySQL, and pain.

Posted by Brian in News, Rails (December 5th, 2008)

Rails 2.2 removes the MySQL native Ruby adapter from its codebase, instead requiring people who want to use MySQL with Rails to use the (better performing) MySQL gem. Unfortunately, Mac and Windows users seem to have some issues getting this installed.

Mac
My original article on this has been updated.

Windows
I’m working on an easy to use tutorial to solve this problem.

“Meet Rails” presentation materials from Chippewa Valley Code Camp

Posted by Brian in Howto, Rails, tips, web (November 24th, 2008)

I gave a talk at the Chippewa Valley Code Camp earlier this month where I built a Rails pastebin application using test-first development principles. Since there were no slides for this, I’ve prepared a small PDF that will walk you through the installation instructions and the coding of the application I built.

Download Meet Rails to get started.

You can grab the source code for the finished project at Github.

Watch this site, as this project may get turned into a full-scale book.

Fixing TextMate for Rails 2.0

Posted by Brian in Rails, Testing, tips (November 7th, 2008)

Rails 2.0 and above have some changes that break Textmate, a very popular development environment for Rails developers.

Running Tests

Rails 2.0 projects generate unit and functional tests with a relative require to test_helper. This change breaks
the Command-R (Run) and Command-Shift-R (Run Focused Unit Test) commands to fail since they can’t
include the necessary helpers. I finally spent a little time figuring this one out and the
answer is to modify the bundle commands themselves.

Open up the bundle editor, locate the Ruby bundle, and choose to edit the &lquot;Run&rquot; command.

Change it from

  export RUBYLIB="$TM_BUNDLE_SUPPORT/RubyMate${RUBYLIB:+:$RUBYLIB}"

to this:

  export RUBYLIB="..:$TM_BUNDLE_SUPPORT/RubyMate${RUBYLIB:+:$RUBYLIB}"

Now edit the &lquot;Run Focused Unit Test&rquot; command and make the same change.

Fixing incompatibilities with Builder

Rails 2.0 contains its own version of builder.rb. We have to take out the original one that TextMate provided
so our stuff starts working again.

mv /Applications/TextMate.app/Contents/SharedSupport/Support/lib/Builder.rb /Applications/TextMate.app/Contents/SharedSupport/Support/lib/Builder.rb.bak

New Rails bundle

Open a new terminal and type this:

  cd "~/Library/Application Support/TextMate/Bundles"
  git clone git://github.com/drnic/ruby-on-rails-tmbundle.git "Ruby on Rails.tmbundle"

RSpec helped me refactor my code.

Posted by Brian in Howto, Rails, Testing (June 10th, 2008)

I’ve been extremely against using RSpec. I always found it rather clunky, but it turns out that resources to really help a person learn how RSpec works are dificult to find. The examples you find out on the web are just poorly written or just contrived and impractical, or they’re so hopelessly overengineered that a newcomer would be overwhelemed.

This weekend I took it upon myself to really learn RSpec and so I started rewriting some of the tests for FeelMySkills. I started with the Account model which is used all over the app. The account_id is stored in the session and I use the Restful_authentication plugin to get access to a current_account method which returns the Account object. I want to be able to determine whether or not that account is an Admin, and I have an entry in the roles called “admin”. Nothing too special about all this, as many apps use a similar bit of functionality.

To make this easy on myself, I wrote a method called is_admin? which returns true if the admin role is associated with my account and nil if it’s not, and as we all know, nil evaluates to false, and anything other than nil or false evaluates to true.

When an account is created, I give them a role called “user”. Eventually, Pro users will have a different role, giving them access to more stuff in the system. Here’s what i have so far:

  class Account < ActiveRecord::Base
    has_and_belongs_to_many :roles
    after_create :add_user_role

    def is_admin?
      self.roles.detect{|r| r.name == "admin"}
    end

    def is_user?
       self.roles.detect{|r| r.name == "user"}
    end

    def add_user_role
          self.roles << Role.user

    end

  end

So, when I create a new user, I need to make sure that user gets the user role. My original Test/Unit test looked like this:

  def test_new_account_should_have_user_role
    account = Account.create(:login => "test",
                                       :password=>"test",
                                       :password_confirmation => "test",
                                       :email => "test@test.com")
    assert account.is_user?
  end

This test passes without any issues, so I know the code is right. Here's what I tried with RSpec:

  describe "when creating an account" do
    fixtures :accounts, :roles
    before(:each) do
      @account = Account.create(:login => "test",
                                           :password=>"test",
                                           :password_confirmation => "test",
                                           :email => "test@test.com")
    end

    it "should have the user role" do
      @account.is_user?.should be_true
    end

  end

Imagine my surprise when I ran this spec and it failed! The reason why makes perfect sense when you start thinking about it.

First, RSpec's matcher be_true evaluates the response of the method to be equal to true. The code for is_admin? actually returns an instance of Role, and not true like I asserted earlier. While that method evaluates to true, it does not equal true. So it's interesting that the assert method has no probelm making the evaluation, but RSpec's matchers are pickier.

A fair argument here would be "why does your is_admin? method return a Role and not just true or false?" The answer is that I'm lazy. I rely on Ruby to work for me, and until now, #detect has been a great ally. In my controller code, I can do

if current_user.is_admin...

and all is well, without the need to explicitly return true or false from the is_admin? or is_user? methods.

A better way

Looking at the spec again, I notice that I am in fact asking for the role in that specification. So I rewrite it to grab the User role from the fixtures and ensure they're equal and it passes.

    it "should have the user role" do
      @account.is_user?.should equal roles(:user)
    end

But something about that bothers me. What am I really testing? I'm testing to make sure that the account is a regular user. Maybe I really do need a method that returns true or false.

It turns out that if you have a method in your model that ends with a question mark (?) and returns true or false, then RSpec can dynamically create a matcher for it. I rewrote the spec like this:


    it "should be a user" do
      @account.should be_a_user
    end

and then added this method to my model:

   # calls is_user? and returns true if is_user? returns a result,
   # or false if it returns nil
   def user?
      self.is_user? != nil
   end

and I ended up with something I am much more comfortable with. I think future refactorings might change this around even more, but I found this exploration to be extremely enlightening.

P.S. For those that are interested, I actually have several roles in my system and I don't manually declare these methods like is_admin? and is_user? by hand. I use this instead:

class Account < ActiveRecord::Base

  # ...

    # constant containing all of the role names
    # in the system
    Role::ROLE_NAMES.each do |r| 

    class_eval <<-CODE
      def is_#{r}?
        self.roles.detect{|role| role.name == "#{r}"}
      end

      def #{r}?
        self.is_#{r}? != nil
      end

    CODE
  end

  #... 

end

That way I don't need to add new methods when I implement factchecker or pro or business roles later. Just thought I'd share that.

Create a new Edge Rails project

Posted by Brian in News, Rails, snacks (April 24th, 2008)

In a previous post, I provided scripts that made the creation of a new Edge Rails project easy. Since then, Rails has moved from Subversion to Git, which means that the scripts I provided no longer work as expected. Fortunately, very little has changed and I was able to make the script a little bit better so that it acts like the original rails command.

Prerequisites

First, you’re going to need git. I could have written the script to grab the latest version via a zipfile, but I wanted something that was fast and worked on all platforms. Windows can unzip files, but then I’d have to make Windows users go grab commandline tools to unzip files.

Installing Git

Mac users with XCode and Macports installed can do it with

sudo port install git-core +svn

Windows users can install Msysgit.

Linux users should install Git using their package manager or from source.
Here’s the script. Instructions for running it are after the code.

#!/bin/ruby
git_repo = "git://github.com/rails/rails.git"

help = %Q{
Rails Info:
    -v, --version                    Show the Rails version number and quit.
    -h, --help                       Show this help message and quit.

General Options:
    -p, --pretend                    Run but do not make any changes.
        --force                      Overwrite files that already exist.
    -s, --skip                       Skip files that already exist.
    -q, --quiet                      Suppress normal output.
    -t, --backtrace                  Debugging: show backtrace on errors.

Description:
    The 'edge_rails' command creates a new Rails application with a default
    directory structure and configuration at the path you specify, using the
    very latest version of Rails.

Example:
    edge_rails ~/Code/Ruby/weblog

    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
    See the README in the newly created application to get going.    

}

require 'fileutils'

if ARGV.empty?
  puts help
  exit
end

dir = ARGV.shift
args = ARGV.join (" ")

FileUtils::mkdir(dir)
FileUtils::mkdir("#{dir}/vendor")

puts "Exporting EdgeRails from #{git_repo}"
# system "svn export http://svn.rubyonrails.org/rails/trunk #{dir}/vendor/rails"
system "git clone --depth=1 #{git_repo} #{dir}/vendor/rails"
system "rm -rf #{dir}/vendor/rails/.git*"

system "ruby #{dir}/vendor/rails/railties/bin/rails #{dir} #{args}"

How this works for Mac and Linux users

Save the script to your home folder as edge_rails, and set the execute bit:

  chmod 644 ~/edge_rails

Run it with

  ~/edge_rails your_app

You could symlink it to your /usr/local/bin if you are feeling clever.

How this works for the Windows crowd

Save this script as c:\ruby\bin\edge_rails.bat

@echo off
goto endofruby
#!/bin/ruby
git_repo = "git://github.com/rails/rails.git"

help = %Q{
Rails Info:
    -v, --version                    Show the Rails version number and quit.
    -h, --help                       Show this help message and quit.

General Options:
    -p, --pretend                    Run but do not make any changes.
        --force                      Overwrite files that already exist.
    -s, --skip                       Skip files that already exist.
    -q, --quiet                      Suppress normal output.
    -t, --backtrace                  Debugging: show backtrace on errors.

Description:
    The 'edge_rails' command creates a new Rails application with a default
    directory structure and configuration at the path you specify, using the
    very latest version of Rails.

Example:
    edge_rails ~/Code/Ruby/weblog

    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
    See the README in the newly created application to get going.    

}

require 'fileutils'

if ARGV.empty?
  puts help
  exit
end

dir = ARGV.shift
args = ARGV.join (" ")

FileUtils::mkdir(dir)
FileUtils::mkdir("#{dir}/vendor")

puts "Exporting EdgeRails from #{git_repo}"
# system "svn export http://svn.rubyonrails.org/rails/trunk #{dir}/vendor/rails"
system "git clone --depth=1 #{git_repo} #{dir}/vendor/rails"
system "rm -rf #{dir}/vendor/rails/.git*"

system "ruby #{dir}/vendor/rails/railties/bin/rails #{dir} #{args}"
__END__
:endofruby
"%~d0%~p0ruby" -x "%~f0" %*

Now, open a new command prompt and type

edge_rails my_new_app

If it doesn’t work, check that you have Git installed properly and have added Git’s command line utilities to your path.

« Previous PageNext Page »