Monday, May 9, 2011

This code is...problematic

It's been a while since my last blog post. Dang, a whole week! Don't worry, though. I never stopped coding.

I'm still having trouble with my Manticore app. Basically, here's what I'm trying to do:

I have two models, Character and Statistic. Each Character will have_one Statistic, and Statistic belongs_to Character. So good so far, right? I want to be able to create the Statistic information from the Character view, then be able to edit that information once there's information to be edited. I want to only be able to create one instance of Statistic per Character, hence the has_one relationship.

One solution I got on Stack Overflow was to simply make a form in the Character view, but since I'll be extrapolating this code (once I finally understand it) to create other models like Equipment, Skills, Spells and so on, it doesn't make sense to load down the Character view with a ton of different forms.

I've been modeling the code for Manticore from this tutorial which has you create a blog. It was easy enough to see that Character could be substituted for the Post model, and Statistic could be substituted for the Comment model.

However, I've hit a wall. Now I get this error:

undefined method `build' for nil:NilClass

And I'm not sure why. I suspect it has something to do with the has_one vs. has_many relationship. Earlier, I'd followed the tutorial more closely, but I need to define the Character/Statistic relationship as has_one vs. has_many because I don't need multiple instances of Statistics per character. Any ideas where this error could be coming from? I'm not defining a build action, but this error never popped up before.

Here's some code, if that helps:

class Character < ActiveRecord::Base  
  has_one :statistic, :dependent => :destroy
end

class Statistic < ActiveRecord::Base
  belongs_to :character
end

As far as I can tell, the model code is fine. But I might be missing something pretty obvious.

Here's code from the Statistics controller.

def create
  @character = Character.find(params[:character_id])
  @statistic = @character.statistics.create.new(params[:statistic])
  redirect_to character_path(@character)
end

I may have something wrong here, but like I said, this has worked in the past and has tied the Statistic to the specific Character (something I was struggling with before this mess!)

This is from the Statistics _form partial:

<%= form_for([@character, @character.statistic.build]) do |f| %>
  <div class="field">
    <%= f.label :strength %>
    <%= f.text_field :strength %>
  </div>
...
    <%= f.submit %>
  </div>
<% end %>

I suspect my troubles may be coming from this code, but I don't get why. I'm following the tutorial and this has worked before, and all I've changed is the has_many relationship to a has_one.

In other less concrete news, I feel pretty dumb for just not getting this. However, in the course of trying to figure it out, both on my own, through people I know and asking and reading questions on Stack Overflow, I've got a much better grip on other things, like the overall MVC hierarchy, how partials work, how views work and so on.

I think part of what's keeping me interested in Ruby and Rails is how difficult it is for me. Coding is not something I have a great natural talent for, so I really have to work to make progress. Still, I'm proud of what I've learned so far and there is no shortage of great, helpful people in addition to a treasure trove of information available, so I'm confident I'll get where I want to be. Sooner or later.

Another problem I've had is describing exactly what I want to do. I think in the future, I'll benefit greatly from mapping projects out better beforehand as well as learning how to describe my ideas better.

8 comments:

  1. Build and create are called differently for has_one relationships than they are for has_many.

    With has_many @character.statistic.build works, but with has_one, it needs to be done like this:

    @character.build_statistic and .create_statistic

    I whipped up an app to test it, and threw it up on Github for you. I hope it helps.

    https://github.com/unixmonkey/Manticore_example

    ReplyDelete
  2. I wasn't even aware of build_*, very cool. Great advice from David and top-notch posting of a test-app.

    ReplyDelete
  3. Another thing, great deduction of where the problem might be(has_one vs has_many). It turned out to be spot on and saved you a ton of time. You're getting good at this awfully fast :)

    ReplyDelete
  4. @David This definitely helps! I'm still having some problems, though.

    I want to keep the pages that add or edit a Statistic separate from the Character view. I imagine this as a button or link that will open a new page, allow a user to make changes, then redirect_to the Character view when finished. At this point, I want to show the information the user just added. I thought changing the statistic/show.html.erb file into a partial and rendering it would do this, but no dice.

    ReplyDelete
  5. @Eli Thanks! I'm fairly proud of myself for realizing the problem, though to be fair I have been spending a lot of time reading up on Stack Overflow and pestering people for help. The first step toward fixing a problem is recognizing it, right? I'm getting better at recognizing problems, anyway!

    ReplyDelete
  6. @Tyler I updated the example app to do what (I think) you are talking about. Let me know if you need any help understanding it. Also, the Indy.rb meeting is tomorrow (http://meetup.com/indyrb). Think about it good sir!

    ReplyDelete
  7. @David thanks again! I took a look at the updated code last night but haven't done much more than that. I'm planning to get some quality time in with Rails tonight, though.

    Obviously I didn't make it to the meetup last night but I'm definitely planning on going next week. Are you going to re:build in July?

    ReplyDelete
  8. I haven't made up my mind on re:build, I'm actually doing a lot of conference hopping this year as it is. But I may change my mind as the deadline approaches.

    ReplyDelete