Incorporation

We incorporated our startup today...a small milestone on a long journey, so we took a picture to commemorate the moment.

HTML and Helpers

Look at all this code. Yikes!

Even though, in theory, you're supposed to be allowed to put HTML in your helpers, the result is that you still end up mixing up all your presentation and business logic. Some people would argue that it's not possible to separate presentation and content. I'm not arguing either way...just trying to point out that it's easy to get these things intermingled.

Anyhow, what's going on here is that a document group has a bunch of document templates. Users will have filled out some templates--in which case the "instances" of those templates become documents--and in other cases they won't have filled them out.

Originally, I was just going to do a condition based on the existence of the document or not, but then I realized this edge case that somehow a user may end up with two documents based on the same template. I'm not sure how this would happen, but could happen easily, for example, if the user bookmarked the URL for the new document page for that document template.

I suppose I could make the new action check for the existence of a document using said template for the current user, and that would almost ensure this problem wouldn't happen...from the UI. What happens if data is added directly to the database? In fact, that's how I found this case...I accidentally loaded some test data twice and my original code crapped out.

Anyhow, in my view:



    <%
    @document_group.document_templates.each do |child|
    array_of_links_to_document_if_exists_from_template(child).each do |link|
    %><li><%= link %></li><%
    end
    end
    %>


And in my helper:


def array_of_links_to_document_if_exists_from_template(document_template)
links = []
documents = Document.find(:all, :conditions => { :journal_id => @journal, :document_template_id => document_template })
if (!documents.empty?)
documents.each do |document|
links.push(link_to_remote(document.name, { :update => "dvMainPane", :url => { :controller => :documents, :action => :edit, :id => document }}))
end
else
links.push(link_to_remote(document_template.name, { :update => "dvMainPane", :url => { :controller => :documents, :action => :new, :journal_id => @journal, :document_template_id => document_template.id }}))
end
links
end

Is the helper the right place for business logic? I'm thinking right solution here isn't to put the logic in a helper...maybe helpers are really just rendering helpers...and that the logic should be in my model.

Navigating Associations in Scaffolding with Toffee

My friend Mark suggested Toffee for dealing with navigating scaffold associations.

Haven't tried it yet, but on the surface seems to solve some of what I was discussing.

Creating Multiple Objects (of different Models) in one form.

Yikes! Did I ever dig hole for myself, so let me tell you how I got out of it.

Long story short: I have a model architecture like this:

document
|
--> document_content_groups[]
|
-->document_contents

In other words, document_contents are name value pairs...think of them as questions (name) and answers (value). one or more document_contents are grouped into a document_content_group...think of that as a chunk of questions. in other words, a document_content_group has a collection of document_contents. then, a document has a collection of document_content_groups.

So, on a form where a user creates a new document really we're creating many things:

1 new document object
1 or more document_content_group objects
1 or more document_content objects

and, all of these must be associated with their parent properly.

On p. 499 the Rails book (Agile Web Development with Rails) it is suggested that something like this be done:

<% for_tag do %>
<% for @product in @products %>
<%= text_field("product[]", 'image_url') %><br />
<% end %>
<% end %>


and what this syntax is supposed to do is make the ID of the object be inserted into the form "name" field, e.g. name="product_ID[image_url]". But (sharing this is the purpose of this post), this syntax does not work with a newly created object, it only works with editing existing objects. I solved the problem like this:


@dcg_index = 0
@dc_index = 0
for document_content_group in @document_content_groups %>
<p><%= document_content_group.name %><br />
<%= text_field("document_content_group", 'name', :index => @dcg_index) %></p>
<%
for document_content in document_content_group.document_contents %>
<p><%= document_content.name %><br />
<%= hidden_field ("document_content", "document_content_group", :value => @dcg_index.to_s, :index => @dc_index) %>
<%= hidden_field ("document_content", "name", :value => document_content.name, :index => @dc_index) %>
<%= text_field("document_content", "value", :index => @dc_index) %></p>
<% @dc_index += 1%>
<% end %>
<% @dcg_index += 1%>
<% end %>


Now, the result is that when the form is posted I get two hashes in the params: document_content_groups and document_contents each of which contains hashes representing the objects I want to create.

I can easily create the document_content_groups by passing those hashes in to create, but I have to do some munging on the document_contents to prepare them:

@dcg_index = 0 # this must be the same initial index from the "new" form
for document_content_group_hash in params[:document_content_group].values do
document_content_group = @document.document_content_groups.create(document_content_group_hash)
for document_content_hash in params[:document_content].values do
if (document_content_hash["document_content_group"].to_i == @dcg_index)
# this is removed from the hash because it's not really the document_content_group_id...
# it's just a temporary ID that was used to group the items together from the _new form
document_content_hash.delete("document_content_group")
document_content_group.document_contents.create(document_content_hash)
end
end
@dcg_index += 1
end

Anyhow, not particularly pretty, but it gets the job done for now. Suggestions on how to make it more graceful are welcome.

Navigating Associations in Scaffolding

In a scaffold generated list.rhtml only the content_columns are displayed. I modified that to show all columns, but the drawback is that for foreign keys you only see the ID, not any thing descriptive. That is, if I have books which belong_to authors, then what I see from the list.rhtml is author_id, not author_name.

I’ve been working to get around that because I can save a lot of time if the joins work magically. So, I wrote some code that basically looks at a column, if it’s an “id column”, then it walks the association to find the object. Once found, it looks for some logical text to display, and if none is found then defaults to the ID.

I've put the code below, but I can’t believe Rails doesn’t provide a way to do this automagically...it seems like something that would be used so often. Anyone?

Anyhow Scaffold makes this:

<% for document_content_template in @ document_content_templates %>

<% for column in DocumentContentTemplate.content_columns %>
<%=h document_content_template.send(column.name) %>
<% end %>
[... snipped show-edit-destroy ]

<% end %>


Which I’ve changed to this:

<% for document_content_template in @document_content_templates %>

<% for column in DocumentContentTemplate.columns %>
<%=h display_column_text(column, document_content_template) %>
<% end %>
[... snipped show-edit-destroy ]

<% end %>


With these helpers:

1  def display_column_text(column, object)
2 if column.name[-3, 3] != "_id"
3 object.send(column.name)
4 else
5 value_from_join(column, object)
6 end
7 end
8
9 def value_from_join(column, object)
10 h = column.name.sub(/_id/, '') # strip off the id
11 r = object.send(h) # get the object
12
13 if (defined? r.name != nil)
14 r.name
15 elsif (defined? r.description != nil)
16 r.description
17 else
18 r.id
19 end
20 end

P.S. Yes, I realize there might be a bug on line 10.
P.P.S There’s actually a potentail bug on line 11 as well…what happens if you have a controller that’s like this:

belongs_to :model_name, :foreign_key => "some_other_id"

which will throw this error:
undefined method `some_other' for #<ModelName:0x47a8650>


UPDATE: the bug on line 11 can easily be handled like so:
begin                               # get the object
r = object.send(h)
rescue
return object.send(column.name)
end


Again, I can’t believe Rails doesn’t the magic to navigate around these objects more gracefully. Thoughts or comments anyone???

Perfection, Not Progress

I wasted an entire day yesterday on :counter_cache => true. (Why was it wasted? Because I didn't need a counter cache yesterday, but knew that I would need it in a few weeks or months...progress versus perfection rears its ugly head again.)

Anyhow the problem ultimately had nothing to do with counter caching, but the error message didn't help too much, and there wasn't much in Google about the error message, so I spent a lot of time looking for the problem in the wrong place. Hopefully this post will help others in the future if you're getting this error message:

NoMethodError in Document content templateController#create
undefined method `increment_counter' for Class:Class

Yesterday I was creating entities called DocumentTemplate and DocumentContentTemplate:
class DocumentTemplate < ActiveRecord::Base
has_many :DocumentContentTemplates
end

class DocumentContentTemplate < ActiveRecord::Base
belongs_to :DocumentTemplate
end

Then, I ran a quick script/generate scaffold on both of these, and modified the _form.rhmtl for DocumentContentTemplate in order to add the association to DocumentTemplate. Everything is working fine.

Next, I decide that I'm going to need a counter cache on the DocumentTemplate, so I modify the DocumentContentTemplate model and added the document_content_templates_count column to the document_templates table via migration:
class DocumentContentTemplate < ActiveRecord::Base
belongs_to :DocumentTemplate, :counter_cache => true
end

Now, when I create a new DocumentContentTemplate the method DocumentContentTemplate#create should automatically increment the document_content_templates_count column in the document_templates table. But, when I submit the form that calls the create method I get this error:
NoMethodError in Document content templateController#create 
undefined method `increment_counter' for Class:Class

I spent hours debugging and investigating all the things related to counter cache, but it turns out that wasn't the problem. Go way back to the has_many and belongs_to declarations:
has_many :DocumentContentTemplates
belongs_to :DocumentTemplate

which, if you look carefully, should really be:
has_many :document_content_templates
belongs_to :document_template

So, if you're seeing that error, that's how to fix it. But, this raises two issues that I want to bring up:

1. My understanding is that the camel-case of a class name is supposed to be used when referring to the class, not an instance of it. For example, camelcase is used in a class definition or when the class is being used:
class DocumentTemplate < ActiveRecord::Base

DocumentTemplate.find(:all)

With that in mind, aren't the has_* and belongs_to methods referring to classes? Why don't they use the camelcase?

2. Regardless of the answer to #1, why does Rails handle this error gracefully? Why does has_*|belongs_to :ClassName not throw an error when it should be has_*|belongs :class_name?

REST

Started designing my app last night. I had most of it in mind, so I just started hammering it out. I know, not the best way to do it, but sometimes you just have to get into the code.

A few days ago, I wanted to get more in sync with Rails and REST before, so I did some reading online. Something I enjoyed was this article (and the subequent parts) from SoftiesOnRails.com.

With REST fresh in mind, design goes really smoothly. There's only one place (yet) in my app where I'm feeling like I can't use one of the seven basic actions provided by a RESTful conroller.

It has to do with a "wizard" model where when a user is creating a new Widget we break it up into a few steps. So, I'm using the create action for the first page of the wizard, and then I guess I need to do some sort of update for the subequent pages. I'm not sure...haven't really researched it yet, but there must be a right way to do this.

RadRails and Scaffold-Migration Best Practice

So, started tinkering again with Ruby on Rails tonight. It's been about a year since I last worked with RoR, so I wanted to familiarize myself with it again.

Fired up RadRails. Let me say that this has undergone vast improvement since I used it a year ago, and I would feel totally comfortable recommending it to anyone. I have been able to manage the entire development process from within the IDE: create and empty application, generate models, views, controllers, and migrations; instantiate, start and stopp servers; rake tasks, database management...so far, I can do everything through the IDE. I'm very impressed.

Then, I spent an hour or so screwing around with a little test app to re-familiarize myself with how the whole thing worked. An issue here and there, but generally speaking it all worked out well.

I'm still trying to get my head around the whole script/generate scaffold thing in relation to migrations. You have to setup the DB table before you can run script/generate scaffold. But, you have to run script/generate model to setup the migration and then run rake db:migrate. This seems feels like I'm doing something wrong because I thought script/generate scaffold is supposed to setup the model too. That's how it was a year ago, in pre-migration days. I have to figure out the best practice now...suggestions are welcome in the comments.

Aptana RadRails IDE and InstantRails

So, I'm doing this new startup thing--can't tell you just yet what the thing is--from the very scratch and Mark asked me keep a blog about it. Here goes...

Yesterday was spent setting up my development environment. In short, this is composed of two things:
  • InstantRails
  • Aptana IDE with RadRails Plug-In
And the key is figuring out how to get the them to play nicely together. So far, I ran into only two glitches:
  1. Installed InstantRails, then installed Aptana IDE, then installed RadRails Plug-In. But, the plug-in wasn't working which I could tell because it's configuration options weren't appearing in the preferences. From reading this post I learned that it turns out I needed to upgrade my Java JDK to 1.5+. Yay. Also, turns out you can't just easily upgrade from within the JDK 1.4 (what I had) Control Panel tool to the latest version. You have to uninstall, find the new package, install, blah...yay.

  2. Got the RR Plug-In working, started reading the RR help documentation and it says there's some configurations that I need to setup. Even though the RR configs are appearing in the Aptana IDE prefs, the specific configs they mention are not to be found in the location where the screenshot in the docs say to look for them. Ug. Maybe the install didn't work...so, uninstall, re-install...everything. No luck. Some posts I read say don't install one sub-package of the RR plug-in (the Ruby Development Tools, RDT)...okay, uninstall, re-install without package...no luck.

    Turns out, this was a bug in the documentation. The configurations had been moved to a new area of the preferences, but the documentation hadn't been updated. Oy!

  3. Finally, This article on the InstantRails wiki describes how to configure RadRails so that they play nicely together. I set the configs and was all set. Hooray!


Note on the title: Progress vs. Perfection...what's it mean?
Some could interpret it as a form of saying quantity is preferable to quality, but that's not what it means to me. In my view, it's about staying focused on the goal while demonstrating respect for the Law of Diminishing Returns.

Just Do It!

Which template should I use? Hmmm...don't know...doesn't matter. That's the whole point of this blog...to make decisions, and move on...without worrying about making a mistake.

Update, July 20, 2007: Just changed the theme...I wanted a serif font...easier on the eyes.