giant robots smashing into other giant robots

Written by thoughtbot

jferris

factory_girl 1.3: integrating effectively with Rails 3

factory_girl has always had little Rails-specific code. It only depends on Ruby, and in order to use it in your application, it asks three things of your models:

  • They can be instantiated without parameters
  • All properties have writer methods
  • They respond to #save! (this is only necessary for the “create” strategy)

However, despite this simple API, there’s one feature in factory_girl that has traditionally caused some issues with Rails: automatic factory definition loading.

The definition loader has simple rules: if it finds any of test/factories.rb, spec/factories.rb, test/factories/**/*.rb, or spec/factories/**/*.rb, it will load those files in no particular order. For most applications, this just makes things a little nicer.

Hacking around Rails dependency management

In early versions of factory_girl, it looked for definitions as soon as you loaded factory_girl.rb. Unfortunately, this broke down when Rails 2.1 introduced gem dependency management. Loading factory_girl during the application initialization phase meant that the factory definitions might reference your models before all the gems and plugins they depended on were fully loaded. Contributer technicalpickles fixed that by detecting Rails and deferring definition loading until Rails was initialized. This solution worked effectively for the entire Rails 2.x series of releases.

Bundler: effective but strict dependency management

The upcoming Rails 3 release uses a new library for managing application dependencies: bundler. Bundler’s behavior is more predictable and understandable than Rails 2’s dependency manager ever was. However, with the introduction of Bundler, two important changes were introduced that impacted many libraries that integrate silently with each other:

  • Rails is no longer initialized by the time your gem is loaded
  • Load order is no longer configurable, so you can’t depend on another gem’s constants being defined when you integrate with it

Yehuda Katz, the author of Bundler, discussed this topic in depth on his blog. In his discussion, he suggests a solution that could be added to Rubygems:

s.integrates_with "rails", "~> 3.0.0.beta2", "haml/rails" 

However, this functionality doesn’t exist yet, so we’ve decided to create a separate factory_girl_rails gem. This way, we can declare an explicit dependency on Rails, including the version, and automatic definition loading still takes place for Rails apps without any additional application code.

What this means for you

This doesn’t change much for an application that uses factory_girl. It boils down to this:

  • If you’re using Rails 2, just depend on the factory_girl gem, and Rails 2 will pick up the existing rails/init.rb and load your definitions.
  • If you’re using Rails 3, just add factory_girl_rails to your Gemfile. This gem depends on factory_girl and will set up definition loading for you.

A lesson on “easy to add” features

I originally added this feature to factory_girl because it was convenient, and seemed easy to add. However, this trivial feature has turned out to be the most difficult to maintain part of factory_girl, and is the single feature that is most likely to outright break an application. I plan on maintaining this feature because we’re used to having it and we’ve worked out most of the kinks at this point, but next time you consider adding an “easy” feature to your own library, also consider whether or not it’s essential to your library’s core mission.

jferris

The future of shoulda

Shoulda has long been one of our most useful and popular open source projects, and it continues to serve us well as we use it daily. However, there have been some changes over the past year in the way that we use Shoulda, and these changes have led to some decisions about its future. In our post about the Rails 3 roadmap, we briefly covered the changes we made in Shoulda 2.11 as well as our intentions for Shoulda 3. However, we wanted to go over these changes in depth and explain some of our motivation.

Putting things in context

Shoulda has two main components: a test context framework, and a set of helpers for testing Rails applications. The context framework was written because we found the method-based style of Test::Unit tedious, but certain objections were raised to the way RSpec was implemented, and we had existing test suites that could not easily be converted to RSpec. The Rails helpers were written in the form of Shoulda-based “macros” for convenience.

While this approach worked great for those of us using Test::Unit, this meant that the Rails helpers were only accessible in Shoulda-based test suites despite relying on very little in Test::Unit or the Shoulda context framework. In order to bring Shoulda’s Rails helpers to a wider community, they were rewritten as a set of RSpec-compatible matchers, and the existing “macros” were rewritten to use them.

A foot in the other camp

As things in the Cucumber and RSpec community started to pick up, we found more and more that Test::Unit users weren’t getting the latest and greatest. Most of the new assertion libraries we found were written as RSpec expectations, and several improvements to RSpec’s context and matcher DSL made it look too appealing to ignore. We tried out RSpec on a few projects, and found that we really didn’t have to give anything up - Shoulda’s macros were fully supported as matchers - and we found ourselves enjoying RSpec more than Test::Unit.

It was at this point that we decided that Shoulda’s future would focus on RSpec. However, we weren’t going to ditch Test::Unit. We had many existing projects that used Test::Unit and Shoulda, and we certainly didn’t want to abandon the fantastic Shoulda community. After several experiments, we decided not to convert our existing Shoulda test suites to RSpec, largely because they didn’t take advantage of all that RSpec had to offer and didn’t come out any better in the end. This meant that we wanted to keep the context framework functioning. However, we also found ourselves reluctant to spend time and effort on a context framework that we didn’t want to use. Therefore, we needed to come up with a way to keep the matchers working with Test::Unit and make sure the context framework continued to function reasonably, but prevent the overhead of maintaining compatibility with two frameworks.

Rails 3

Rails 3 presented another challenge. Although some of the existing matchers just worked, the general strategy for testing them had to change with Rails 3, and some matchers required conditional forks in the implementations and tests. This was more complicated in the existing macro test suite, and maintaining two test Rails roots for backwards compatibility could potentially slow us down and make it harder to add new features. We preferred the unit-style tests in place for the matchers, and wanted to avoid the rails_root-style test going forward if possible.

Embracing matchers

After refactoring all of the macros to use matchers, we found that the macros all followed a basic pattern: convert a number of hash options into RSpec’s “fluid” syntax, and then create a test that performed one assertion based on a matcher. Based on this discovery, we changed Shoulda’s should method so that it could accept a matcher instead of a test name:

# macro style
should_have_many :users
should_ensure_length_at_least :name, 3
should_not_allow_values_for :isbn, "bad"
should_set_the_flash_to /thank you/i
should_not_assign_to :user

# matcher style
should have_many(:users)
should ensure_length_of(:name).is_at_least(3)
should_not allow_value("bad").for(:isbn)
should set_the_flash.to(/thank you/i)
should_not assign_to(:user)

Converting to this syntax would mean that we no longer needed to support both a set of macros and a set of matchers - all matchers essentially function as macros. In fact, this includes most non-Shoulda macros. We currently support both syntaxes, but you’ll receive a deprecation warning for the older macro style. We’ll also be removing the macros entirely starting with version 3, so now is definitely the time to start embracing matchers.

Separating Shoulda’s contexts

We’re committed to using RSpec, so we don’t want to spend time adding features to Shoulda’s context framework. However, we need it to continue to function for our older test suites, and we understand that some users still prefer the lighter weight Test::Unit framework.

Therefore, we plan on separating Shoulda’s context framework into a separate library starting with version 3. The contexts will still function as always, but we have no plans to further improve this aspect of Shoulda.

Putting it all together

There are a lot of changes here, but they’re not too hard to understand:

  • Shoulda is now Rails 3 compatible!
  • Shoulda is focusing on the RSpec/Shoulda combination and will primarily support that combination of tools, moving away from Test::Unit
  • Shoulda’s “macro” methods are deprecated in favor of using the new should/matcher syntax
  • Shoulda’s context framework is moving into a separate library

We’re looking forward to Rails 3 and RSpec 2, and we hope you’re looking forward to Shoulda 3.

cpytel

The Road to Rails 3

Being at RailsConf 5 has given us the opportunity to finalize a lot of the work we’ve done to prepare our plugins and gems for Rails 3. Thankfully, for many of the most popular gems, we’ve been able to maintain both Rails 3 and Rails 2.3.x compatibility in one gem.  However, we’re taking this opportunity to say goodbye to some of our less widely used plugins, and some we plan on dropping Rails 2 support for altogether.

Obviously, Rails 3 isn’t actually out yet, so what we’re talking about here is Rails 3 beta 4.  We’ll continue to keep things up to date and tested as we all move toward the release of Rails 3.  Your help and patches are more than welcome.

So here is a comprehensive overview of the current status of the projects for both Rails 3 beta 4 and Rails 2.

Paperclip

We released Paperclip 2.3.3 a few days ago. This new version of Paperclip will work with Rails 3. Thanks to the investigation of nragaz and help from isaac and joeljunstrom on github, we worked out the kinks and it should be working with the Rails 2.3.x line, and Rails 3-beta 4.  For the latest version of Paperclip, we’re no longer officially supporting Rails 2.0.x. The earliest version that will work is Rails 2.1.0.  If you need support for an older version of Rails than that, you can use Paperclip 2.3.1.1.

hoptoad_notifier

A few days ago we released hoptoad_notifier 2.2.6 with includes support for Rails 3-beta 4 as well as all versions of Rails 2.x and Rails 1.2.6. 

shoulda

We just released shoulda 2.11.  Along with Rails 3 support, we’re maintaining support for Rails 2.3.x in this latest release.  However, the latest version of shoulda will not support versions of Rails less than 2.3.  If you need support for a version of Rails older than that, you can use a previously released version.

In addition to the Rails 3 support, shoulda 2.11 introduces some dramatic changes to shoulda, including a new way of interacting with all shoulda macros.  The previous way has been deprecated and will be removed in shoulda 3.0.  We’ll make a separate blog post detailing many of the very cool changes to shoulda and more details about the future of shoulda soon, but for now, take a look at the README for the latest information on setting up and using shoulda.

Factory Girl

We just pushed factory_girl 1.3 and factory_girl_rails 1.0.  This new version adds Rails 3 support.  Because of the way that Rails 3 loading has changed, we’ve decided to make a separate factory_girl_rails gem that will be used for when you want to use factory_girl with Rails.  The existing factory_girl gem is used by factory_girl_rails and would be used if you’re using factory_girl outside of Rails.  If you want to use factory_girl with Rails 2 you can continue to use the base factory_girl gem.

Clearance

We just released Clearance 0.9.0.rc1.  This is a release candidate for Clearance 0.9.0.  This new version adds support for Rails 3 but drops support for Rails 2.  Don’t fret, if you won’t be upgrading to Rails 3, you can use a previously released version of the gem (0.8.8).  We’re doing this one as a release candidate because of the dropping of backwards compatibility and the fact that we haven’t had a chance to test the new version in a variety of Rails 3 apps using clearance.

Please flex this release candidate with your Rails 3 apps and let us know how it goes.

Suspenders

Suspenders is currently at 2.3.5 (we haven’t been able to upgrade to 2.3.8 because of bugs we’ve seen with mongrel, webrat, and rack).  We anticipate that Suspenders will be upgraded to Rails 3 a little after Rails 3 final comes out.  But to be honest, we’re actually not sure yet what the upgrade path will look like for applications that are currently tracking Suspenders.  It may be impossible to do without so many conflicts that its not worthwhile.  We’re going to have to work on this more and keep you posted.  Additionally, we’re in the process of making some fairly dramatic changes to Suspenders.  Watch it on github and stay tuned here for more.

High Voltage

Fire in the Disco! We’ve also released High Voltage 0.9.0 which supports Rails 3 and is now a gem (it was previously just a plugin).  The new version also drops support for Rails 2.  If you need the previous, Rails 2 plugin there is a rails2 branch you can retrieve it from.

Pacecar

We also just released Pacecar 1.3 which supports Rails 3 and drops support for Rails 2.  As in the other cases where we’ve done this, you can use the previous version of the gem, version 1.2.0 with Rails 2, or track the rails2 branch.

Squirrel

Squirrel was born out of a desire to make a new query syntax that was dynamic while being clean and simple.  With Rails 3’s introduction of the New Active Record chainable query language, that goal has now been achieved in Rails.  As a result, we’ll no longer be maintaining Squirrel.  It was a fun ride.

Mile Marker

Over time, our workflow slightly changed for how we built applications and we haven’t used Mile Marker ourselves for some time now.  As a result, we’re taking this opportunity to cease maintenance of this plugin and bid it farewell.

Moving on down the road

We’ve gotten more and more familiar with Rails 3 during moving all these gems to it.  Many of the new features it offers are great, and existing features have been improved and cleaned up.  We’re looking forward to Rails 3 finally being released in the coming weeks.  Now that our plugins are up and running it should help us all to transition smoothly and quickly.

Thanks to the core team and various other railsconf attendees for spending time with us this week working on some of this - we’re looking forward to the final version of rails3!