I recently had to put together a particularly ugly web form, with dynamically-expanding multi-nested elements, and came up against some rather odd behavior from Rails’ nested forms, using Rails 2.3.8. First off, I have to give thanks to Railscasts for saving me a bunch of time creating the dynamic portion of the nested form — see that episode and the following one for a great solution which got me started. Secondly, note that in Rails 2.3.5, some nested forms behavior was simply broken, and when I upgraded to 2.3.8 it fixed a number of small issues.

Unfortunately, for the form I was working on I had to nest two levels deep, which complicated things. The relationship was something like this: the form was created to update an event, which can involve multiple companies. For each involved company, there is additional metadata. So there are three models involved: Event, CompanyEvent, and Company. CompanyEvent is more than just a join model, since it contains metadata about the relationship. In theory, the nested-nested models weren’t a problem, simply by putting the proper directive in each model:

class Event < ActiveRecord::Base

  has_many :company_events
  accepts_nested_attributes_for :company_events, :allow_destroy => true

class CompanyEvent < ActiveRecord::Base
  belongs_to :company
  accepts_nested_attributes_for :company
  belongs_to :event

class Company < ActiveRecord::Base
  has_many :company_events
  has_many :events, :through => :company_events

Thanks to the accepts_nested_attributes_for directives, the form for an event can easily incorporate entries for company events, which in turn incorporate companies. As an example:

<% form_for(@event, :url => event_path(@event)) do |f| %>
  <%= f.text_field :event_title %>
  <% f.fields_for :company_events do |builder| %>
      <%= builder.text_field :company_role %>
      <% builder.fields_for(:company) do |company_form| -%>
          <%= company_form.text_field :name %>
      <% end -%>
  <% end -%>
<% end -%>

The above is a slimmed-down form, of course, but serves to demonstrate the nested form approach. The event form is the outer one, which contains fields_for the company_events model — each current instance of a company_event associated with the event will be rendered with its company_role (for example, what role the company plays at the event in question). Within that nested form, another fields_for is included for the company, which will pull in the company name as a field.

As shown in the Railscast linked above, you can also include a field to mark a nested entry as deleted, such as . Check out the Railscast for a full demonstration, since there’s no need to repeat it here.

The issue I encountered, though, was mysterious: when I tried to change the name of a company in the nested form, I got an error: “Couldn’t find Company with ID=12345 for CompanyEvent with ID=6789”. This didn’t make much sense, since obviously there wouldn’t be a matching entry, because I was changing the company and thus the company id would have also changed! It was a mystery why the code would be trying to find a matching row using both ids. I actually had to go into the code for nested_attributes.rb and look into the assign_nested_attributes_for_one_to_one_association method to see what was going on. The key to it seemed to be the use of the :update_only option on the accepts_nested_attributes_for directive. I added that to the CompanyEvent model:

class CompanyEvent < ActiveRecord::Base
  belongs_to :company
  accepts_nested_attributes_for :company, :update_only => true
  belongs_to :event

And suddenly it worked. The slim documentation for the :update_only option wasn’t very helpful (see, as it says that “an existing record may only be updated” and “A new record may only be created when there is no existing record.” Which seems rather obvious, but almost implies that a record can’t be deleted, since it’s “update only”. Otherwise, why on earth would you not want an existing record to be updated? Perhaps this should be the default behavior, though I haven’t tested to figure out what the alternative really means. I need to look at Rails 3 and see what’s changed about the nested forms behavior, and perhaps this is mapped out more clearly there.

In any case, perhaps this will save someone else some pain, since it took me some time to work out what was going on. And I realize that I’ve skimmed over a lot of details of how to do nested forms, since this isn’t intended to be a how-to but more of a watch-out post. If anyone thinks that a general nested-forms how-to post would be useful, let me know and I can put one together.

Here’s an oddity that we just ran across this afternoon, and it’s a nice bit of Ruby trivia to break the bit of silence here on the blog. It has to do with using an ordinary each on a Ruby array, and what happens if you change the array while you’re iterating. Here’s an example in irb:

irb(main):004:0> s = [1, 2, 3, 4, 5]
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5
irb(main):005:0> s.each do |i|
irb(main):006:1* puts "> #{i}"
irb(main):007:1> s.delete(i) if (i == 3)
irb(main):008:1> end
> 1
> 2
> 3
> 5
    [0] 1,
    [1] 2,
    [2] 4,
    [3] 5

As you can see, we first create an array containing the numbers 1 through 5. Then we do a simple iteration, and print out the value of each array element. If it’s element 3, then we delete that element from the array, and continue on.

What you might expect to happen is that it will print all of the entries, 1 through 5, and at the end the array will simply be missing element 3. That would make sense. However, it’s not what happens. As shown above, instead we end up skipping element 4 altogether!

While unexpected, there is some logic to why this happens. While we iterate, Ruby is essentially holding a pointer, or an offset, into the array. When we’re on element 3, the offset is 2 (counting from zero). When we delete element 3, the array shrinks, but the offset clearly is left at 2. When we continue our iteration, Ruby increments the offset to 3, which ends up pointing to the value “5” because we deleted one element. That means that we end up skipping the value “4”.

We happened to encounter this problem while working in one of our models in a method that needed to delete all the elements in an associated model, and we had code that basically did the following:

other_models.each {|m| other_models.delete(m)}

The test data had three other_model entries, but every time it deleted the first and the third, leaving the second one. As we discovered, that’s because deleting the first one meant that the array shrank, and the each ended up skipping the second element.

It’s hard to say whether this should be considered a Ruby bug or not, but I’m feeling a bit inclined to say it is…

I stumbled on this in a random email list archive, and modified it slightly, but I certainly can’t take credit for it. In any case, it’s a really nice way to see what the environment you’re running in looks like so I thought I’d share. For example, I’ll run this in script/console to verify things like the Rails and Ruby version. It can be quite handy:

>> Object.constants.sort.each {|c| cv=Object.const_get(c); print c, "=", cv, "\n" unless Module === cv}; true
RUBY_COPYRIGHT=ruby - Copyright (C) 1993-2010 Yukihiro Matsumoto
RUBY_DESCRIPTION=ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
=> true

I put the “true” at the end just to prevent it from spewing out the object info.

I have finally had the chance to update my Redis cheat-sheet so that it has the latest commands, including the Hash, Multi/Exec, and Pub/Sub stuff. I’m calling this v2.0 of the cheat-sheet since there are lots of changes and it in theory puts it in-sync with Redis 2.0, though I’m sure there will be small changes to come.

I’ve also done the right thing and created a new GitHub repository for it, with the OmniGraffle file there (though not the fonts, yet, pending looking at them to see if that’s okay). So, enjoy, and let me know if I’ve missed/goofed anything, of course. The repo is here and the direct link to the PDF if that’s all you want is here.

I just pushed a new project to github, redis_logger. I decided to give this a go and see if it ended up as potentially useful as I thought it might, and I’m pretty pleased with its initial version, limited though it is.

The idea is that by installing the tiny gem you can add logging into Redis to your application, including the ability to group log entries together, and then browse the groups, including intersections between groups.

As an example: let’s say you do like I did, which is to add request logging to your Rails application. I added this to my application_controller.rb:

  before_filter :log_request

  def log_request
    RedisLogger.debug({ "request" => request.url,
                        "remote_ip" => request.remote_ip,
                        "user_id" =>,
                        "username" =>
    }, "requests")

This will create a log entry as a Hash in Redis, containing the key/value pairs that were passed in. A “timestamp” value is also automatically added for you. This entry will be added into two groups, “debug” and “requests” — the “requests” group is passed into the call as the optional second parameter.

I could also add a call in my controller to log a warning if a user tries to access a page and is redirected to the signin page, or an error if an exception is passed up. I could then view all of the log entries in the intersection of “error” and “requests” to see only errors logged in the “requests” group, and not the debug or warn messages.

One of the great things about this is that it was so easy to do using Redis, once I worked out the approach. Using Sets, the code stores the entries and then adds them to the log groups as sets. Using Hashes, each entry is stored with its key/value pairs intact. I’m thinking about things l ike adding some more standard keys, like the current “timestamp” key, and enabling some additional functionality like adding tags for additional searchability within the groups.

Having the log entries in Redis is great, but browsing them is the fun part. So, there’s also redis_logger-web, a simple Sinatra app that lists the groups and lets you view the log entries. You can click on a group name to see its entries, most recent to oldest, or you can select multiple groups and view the intersection of their entries. Right now that’s limited to just the most recent 100 entries, until I work out the best way to save temporary sets and clean them up in a cron. In reality, though, the most recent 100 entries is what’s generally useful. Adding export functionality is first on my list, because that will be extremely handy for analysis.

Scaling, testing, and adding the essential multi-threading are next, of course. Sound interesting? Please, grab it, fork it, enhance it, let me know what you think.

I’ve just had a few days to play with the iPad and I’m pretty impressed so far. But after spending some time with iBook, and thinking about the potential audiences, it seems pretty clear to me that Apple has missed one huge audience. But they could fix this very quickly and turn the iPad into an absolute must-have for students.

I was thinking back to when I was in high school and college. Like most students, I spent a great deal of time reading textbooks, and taking notes. Perhaps you already see where I’m going with this. I turn on my iPad, and start reading a textbook. So far, so good, it’s a great reading experience. But as I’m reading I want to annotate — but more than simple annotations, I want to highlight passages, write my thoughts, and collect all of that information. So here’s my suggestion for Apple, to make this experience unbeatable.

I want to be able to read a book in iBook, and turn on a sidebar for notes. If I tap in the sidebar, I should get the keyboard to enter my notes, then tap the book to hide the keyboard again. I should also be able to select a passage in the book, and drag it into the sidebar, where it should be automatically enclosed in quotes. Then I can enter notes about the passage. It should also automatically put the page number of the passage in parentheses, so when I’m reviewing my notes later I can find the passage again.

Then, when I’m done reading the book, I should be able to click a button and have all of my notes, with any quoted passages, exported to iWork Pages, so I can format them and create my term paper.

How hard would it be for Apple to add this functionality? Probably be a week’s work for a developer, I would expect. Nothing magical here, just common sense. Oh, and for anyone who’s going to say that you could select the whole book, export it to Pages, and pirate the book, sure — have it limit any quoted passage to a paragraph. You want to copy a whole book, one paragraph at a time? There’s no stopping crazy people, go for it.

Apple, are you listening? Do students a favor, and change their lives with this.

I put together a cheat-sheet for Redis, so that I didn’t have to keep a browser tab open on the commands page all the time. This is v1.0 — please let me know any suggestions, ideas, critiques, etc. There are a number of exciting new commands coming (hashes! multi!) that I will add as they become available. I hope this proves useful for people.

Download the PDF.


Get every new post delivered to your Inbox.