Thursday, November 29, 2007

links for 2007-11-30

Thursday, November 15, 2007

Friday, November 9, 2007

Functional Programming Meets Web Application Development

If you're checking out functional programming, but are firming stuck in object oriented programming, then give Scala a try. Scala merges aspects of OOP and FP together into a cohesive and flexible language.

If you're a web application developer interested in functional programming, then you'll want to check out David Pollak's presentation on lift to the Bay Area Functional Programmers.

lift is a web framework written in Scala, using both functional programming and Scala's Actors, which enable heavily concurrent applications via lightweight (threadless) processes.

Thursday, November 8, 2007

links for 2007-11-09

Rails Bug With Has Many Collections Empty? and Build

After my long rant about problems in Ruby on Rails has_many collections and empty? calls, we spent some more time and really condensed the problem. I can explain this problem much more clearly now.

Let's take the following code:


f = Foo.new
f.bars.build
f.bars.empty?
pp f.bars


What do you think will be printed out when we pp f.bars?

Well, if you're like me, you'd think it would return an array with one Bar in it. And, if you were like me, you'd be wrong.

Turns out that the call to f.bars.empty? doesn't realize you've ever added a new Bar to the collection of bars. Therefore, it will go into the database and do a select count(*) to determine the size of the collection. It will return zero, because at this point you haven't saved anything into the database. Not only is going to the database to determine the size of the collection wrong (in the line above, I've added a Bar though build so empty? should know there's at least one), but empty? in the process is caching the empty collection!

Because of this caching, our last line of code here is printing out an empty array. This effectively deletes the original Bar we added via build.

This has to be a bug. In fact, I'm filing a bug at Rails' Trac system.

How do you work around this? Just change your usage of empty? to length.zero?:


f = Foo.new
f.bars.build
f.bars.length.zero?
pp f.bars


The above code will work just fine!

Many thanks to Wenyi for researching this with me.

Sunday, November 4, 2007

When Empty Isn’t Empty With Rails Active Record Collections

So I'm writing some Rails code, just playing around, and I run into a very strange situation. I've found a situation where Rails' Active Record code is calculating the results to empty? incorrectly, even if I add elements to the collection. So I had a has_many collection with elements inside of it, but calls to length or empty were returning 0 and true.

Here's my simple models:


class Query < ActiveRecord::Base
has_many :measures
validates_presence_of :cube_name

def measure_attributes=(attrs)
attrs.each {|a| measures.build(a)}
end
end


and


class Measure :query
has_one :condition, :as => :conditionable
end


Notice that in Query, I've added a measure_attributes method which automatically handles the construction of new Measure instances from the form parameters.

Here's the simple nested form. From the models, a Query has many Measures:



<% form_for :query, :url => {:action => :new} do |f| %>
Cube Name: <%= f.text_field :cube_name %>

<% @query.measures.each do |measure| %>
<div>
<% fields_for \"query[measure_attributes][]\", measure do |mf| %>
Measure Name: <%= mf.text_field :name %>
<% end %>
</div>
<% end %>

<%= submit_tag %>
<% end %>


Here's what I was doing in my controller. Again, very simple:


class QueriesController < ApplicationController
def new
@query = Query.new(params[:query])
@query.measures.build if @query.measures.empty?
end
end


Now, I wouldn't normally have the new method handle it this way (I would have both a create and a new), but this was test code. My check against empty? was basically saying "if this is the first time I've hit this method, throw in a blank Measure so the form will show at least show one Measure."

Well, it turns out, that call to empty? is calculated by checking the database! Here's the actual query:


SELECT count(*) AS count_all FROM measures WHERE (measures.query_id = NULL)


Even though I added Measures via measure_attributes, ActiveRecord here doesn't seem to acknowledge that at this point.

When I rendered the new template after the form is submitted, the loop in the RHTML (@query.measures.each) doesn't iterate, because it thinks there's no measures.

Sigh.

So, here's what we learned. Don't call length or empty? on an Active Record managed has_many collection unless you understand how it's calculating the size of the contents (via the database). (Also, I still don't quite understand when it goes to the database and when it uses the actual items I've manually added.)

Here's the best way to handle this situation, completely avoiding the call to empty?:


class QueriesController 'new'
end
end