Archive for the ‘Uncategorized’ Category

Rails, Nested Forms and collection_select

I spent a bit of time on this tonight and thought I’d post about it.

I have a Property model that has a has_one relationship with an Address model. I want to be able to CRUD these two objects as one single entity, however. Luckily, nested forms were recently introduced into Rails 2.3.2 and they seemed like they’d simplify the whole process. Which they did once I put everything together. First, my models:

class Property  true
end

class Address < ActiveRecord::Base
  has_one :property
  # the Address model has a :city string column that will get populated from
  # a drop-down list in our view
end

This is the basic stuff that any tutorial on nested forms in Rails will tell you about, with the exception of creating a default instance of the address in the property constructor. This is most likely specific to the has_one case. Most of the examples I have seen deal only with has_many, where behind the scenes rails creates an empty list and we’ll never end up with nil reference errors in our view. This was the first issue I had to solve. Now let’s move on to our new.html.erb view:


  
  

The trick here is to make sure you qualify the collection_select for cities with the specific fields_for instance. If you don’t, the form will render and appear to be ok until you post to the create action or view the generated source:

notice how I've removed the af. qualifier on collection select, and added in a new parameter :address. This is how most tutorials show you how to use it The problem with this is that the rendered html does not generate properly for our nested form scenario

Once you qualify the collection_select helper properly, you should get generated html that looks like this:

City
        Lethbridge

Now when you post the form, Rails nested form magic just works. My controller looks just as you’d expect (ie; no different than the non-nested form scenario)

  def create
    @property = Property.new(params[:property])
    respond_to do |format|
      if @property.save
        flash[:notice] = 'Property was successfully created.'
        format.html { redirect_to(@property) }
      else
        format.html { render :action => "new" }
      end
    end
  end

Hopefully I’ve explained this properly. Feel free to ask questions in the comments if I haven’t.

Advertisements

Extending clearance

I thought I’d try using clearance to handle authentication on the Rails app I’m developing. It seems pretty nice and basic, but I wanted to add a :name property on the user model (not for authentication, just for display purposes).

Once I’d created a migration to add the new column to the user model, I needed to add the field on the corresponding view so it could actually be input by a user. Since clearance is installed as a vendor gem/engine (still not sure what the difference is) the view files are located in the vendor directory. I figured updating these files wouldn’t be good, since they’d be overwritten if I ever had to update and unpack the gem.

It turns out all I had to do was copy the view files into my app under the views/user/ directory. I could then modify the files at will, and they would be used by Rails instead of the vendor files. One other gotcha was to make sure to add the attr_accessible :name declaration in my user.rb file (so Rails can do a mass assignment of the posted form items). My user.rb ended up looking like this:

class User < ActiveRecord::Base
  include Clearance::User
  has_many :properties
  attr_accessible :name
end

Rails Functional Testing Gotcha

I just started writing my first real Rails app this weekend, and as I struggle along I thought I’d document where I’m tripping up.

My first order of business is to write functional tests on my controllers and views. For some reason I thought I’d give the tests (which inherit from ActionController::TestCase) descriptive class names like PropertiesDashboardTest instead of PropertiesControllerTest. I had tests running and passing before I renamed the test class, but then started getting this error:

RuntimeError: @controller is nil: make sure you set it in your test’s setup method.

It took me a while to realize that Rails auto-instantiates a controller instance based on the test name when you inherit from ActionController::TestCase. Since I don’t have a controller named PropertiesDashboard.rb, Rails can’t set one up in the test for me. Of course I’m doing this test-first, so a controller doesn’t even exist at this point. That also explains why my existing test started failing when I renamed it.

Forgetting Craftsmanship

Sergio Pereira¬†has just posted a video of a speech by Bob Martin about¬†Extreme Programming after ten years. It’s a fascinating presentation with a message that resonates with me in particular, since I’ve been a part of a software project for the past 3 1/2 years that has left craftsmanship behind while still keeping to the trappings of Agile development (SCRUM in particular). The result has been tragic (to the code base), and the more people who learn from this, the better. Bob may have just inspired me to try to tell the story. But for now… Just watch the video.

Winter

I think it finally came. Makes riding difficult.

iBelt

I forgot my belt this morning. Luckily, my iPod cable just happens to be the perfect length to substitute. Nerdy? Whatever. At least I have pants.

My Cycling Commute

This past June I started cycling from my house in Evergreen (in the SW) to downtown Calgary. Today I took the time to create a Google Map of the journey:

View Larger Map