things i learned the hard way | V.
First things first. When you make models and then relate them to each other using :belongs_to and :has_many you automagically add a _*method*_ that is the name of your class in your model to all of the other models it has a relationship with.
Yes, I know this is 1A Rails stuff but I don't learn as fast as you big brains out there. I started on this journey in 1996 making label tags to stick on furniture for presidents day sales with one ink color while an angry marketing director denounced my lack of moxie. It's a long way from learning how to use the pen tool in illustrator 5 to here.
Now that things are really weird here's some code:
I have a *Project* model and an *Hour* model. I have a _projects_ table and a _hours_ table in my database. I use:
in my project model and:
in my hour model.
This is why I can apply the _hours_ method to my @project array of objects. ActiveRecord does the hard stuff. Now I wanted to get the total of all the hours for a project. But how to do this?
At first I tried looping and trying to add each hours_billed to the next one in the array but I kept getting a fixnum error, or being told that it can't write string to whatever. Yeah, I'm a big dumb monkey hoping to be the one in a million to randomly reproduce the works of shakespeare by banging on my powerbook while shrieking and flinging my poo.
I eventually opened my picaxe and found something called *inject*. This looked promising. But I, in my unique way, visualized my @project array as needing to be fiddled with to extract the column I wanted and get the value that was inside of it. I tried:
Thinking this is how it should work. I mean when you loop through your instance variables in your views you access the attributes of your object by putting _object.your_column_ so I thought this was the way to do it.
*_WRONG!*_
I kept getting a noMethodError because there was no method called *billed_hours* in my model. WTF? After I inquired at the irc room #rubyonrails about how to do something like this I was directed back to the *inject* method, except this time I got the little nugget of enlightenment I so desperately needed. Check it out:
Holy shit! It worked!. I was trying to force it before it was ready (that's what she said) and all I needed to do was understand how you use the *inject* method to iterate through a collection of objects. My assumption of needing to extract the attribute before the block was wrong _wrong_ _*wrong*_! You feed your collection of arrays into the block AND THEN call the attribute you want from each array inside of the block statement and as it evaluates each array that passes through your magic walking fingers machine it will add the stuff you tell it to and when it gets done you have a total.
I plan on extrapolating this knowledge into more understanding and eventually prettier code. I'm always getting ahead of myself, and this was just one in a lifelong string of examples.
The pragprog books use encouraging, accessible language to explain and educate someone who is using their materials to learn Ruby and Ruby on Rails. I know the words are giving me the information I need.
The problem comes when my internal parser bends the words into a funhouse mirror reflection of their literal meaning and then I find myself feeling like I'm 2 steps behind and I missed the base abstractions that everyone else just knows because why wouldn't you know that, stupid?
I'm fighting 3 battles at once with this stuff. I'm fighting the learning disability with language, the attention deficit disorder, and the peculiar emotional twists that can result from 30 years of living with the first two.
I think in dependencies and relationships. Everything has a cause and effect. I've always understood that information is constant, it is just it's form that changes. The major hurdle has been translating the syntax into jeremy-speak and converting external information to internal understanding while avoiding the frustration and despair these activities can produce.
So now I know you can get at the attributes of your object (the columns in your rows) inside the block that the inject method expects as an argument.
Next.
@project = Project.find(params[:id]) @hours = @project.hours.find(:all)
has_many :hours
belongs_to :project
@total = @hours.billed_hours.inject(0) {|sum, element| sum + element}
*_WRONG!*_
I kept getting a noMethodError because there was no method called *billed_hours* in my model. WTF? After I inquired at the irc room #rubyonrails about how to do something like this I was directed back to the *inject* method, except this time I got the little nugget of enlightenment I so desperately needed. Check it out:
@total = @hours.inject(0) {|sum, element| sum + element.billed_hours}