Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

PIVOTAL LABS
Standup for 2/3/2009: Enemerable#sum vs ActiveRecord#sum

Interesting Things

  • When you call user.purchases.sum(), you are invoking ActiveRecord::ClassMethods#sum rather than Enumerable#sum. If you want to invoke Enumerable#sum (which takes a block and is more powerful though less performant), you’d have to call user.purchases.target.sum() {|p| p.price * p.quantity}.
  • NewRelic sometimes makes our app servers malfunction. Several of us reported having these sorts of problems on different projects. It’s always fixed by the NewRelic team with a new version or a configuration change, but we wish that we felt safer about our production server stability. Some projects feel that the value is certainly worth it, and Engineyard uses NewRelic data when discussing scaling, so it’s worth hanging in there.

Ask for Help

  • What’s the deal with using the OSX terminal and bash/readline messing with the terminal? We’re always typing some ridiculously long command and bash starts writing over itself. Especially when we use Ctrl+A and Ctrl+R and edit the line. Anyone know how we can stop/fix this once it happeneds?

Comments
  1. Mark Wilden says:

    If I use #target that way, I get an empty array.

    Challenge.first.participations returns an Array full of records.

    Challenge.first.participations.target returns an empty Array.

    I don’t find any documentation for #target.

    Any thoughts?

  2. JP says:

    #target doesn’t automatically load the association records. Once the records are loaded, #target returns them in an Array.

    I’ve gotten around the same problem using #to_a which results in the target being loaded first.

  3. Matthew O'Connor says:

    I believe “stty sane && reset” will temporarily fix the OS X bash issue. There is some problem with Pivotal’s workstation image that this bug happens all the time. I rarely see this problem on non-Pivotal machines; maybe once every 6 months and usually it’s b/c some program crashed and didn’t reset the terminal correctly. At Pivotal the command line would get confused many times a day.

  4. Adam Milligan says:

    Regarding #sum, you’ll need to force the association to load, if it hasn’t loaded already:

    user.purchases(:load).target.sum {|p| p.price * p.quantity}

    Note that the parameter to the association proxy is a boolean, so you can make it anything you want, other than false or nil. I like to put something nice and descriptive in there, because it makes me feel warm inside.

    As for all this #sum nonsense, I really think that the Rails team could have come up with a different name for the ActiveRecord method. If you simply call

    user.purchases.sum

    you get an error message about wrong number of arguments (0 for 1). Hardly obvious why trying to use a method build into the Ruby standard library would fail in this way.

  5. Chad Woolley says:

    Brian and I found the cause of the Mac terminal error. It was the custom PS1 line at the bottom of ~/.bash_pivotal on the image. It contained escape characters which did bad things. Deleting it should fall back to the default OSX PS1.

    We’ve commented this in the template, but you may need to svn update /usr/local/tools and/or manually delete it from ~/.bash_pivotal depending on your image version and whether you are still running nightly.sh (which is all machines except very new images).

  6. Mark Wilden says:

    Thanks for the info about forcing the association to load. With the code I used above, instead of

    Challenge.first.participations #=> buncha records
    Challenge.first.participations.target #=> bupkus

    I should have tested it with

    c = Challenge.first
    c.participations #=> buncha records
    c.participations.target #=> buncha records (in a real Array)

  7. Adam Milligan says:

    Mark,

    The example you gave is interesting, but the reason it works the way it does is potentially subtle. For those reading along at home, here’s the play-by-play:

    Challenge.first.participations #=> buncha records

    This loads the first Challenge object from the database and returns the association proxy object for the participations association. Presumably you’re looking at this in IRB, which had to load the objects in order to print out the result of the line. If this were not IRB, the association would not load the objects until you tried to access them (e.g. by iterating over them).

    Challenge.first.participations.target #=> bupkus

    This loads the first Challenge object from the database into a new in-memory object, asks for the association proxy, and then asks the proxy object for its target. As mentioned above, asking a proxy for its target doesn’t cause the proxy to load the contents of its target, so you get an empty array.

    The second code block does the same thing, but, of course, the local variable means you’re asking the same in-memory object for the proxy (line 2) and the proxy target (line 3). But, keep in mind that in non-IRB ruby code line 2 won’t load the objects in the association, so line 3 will still return an empty array.

  8. Adam Milligan says:

    Also, regarding my example from a few comments ago about forcing the proxy to load its associated targets:

    user.purchases(:load).target.sum {|p| p.price * p.quantity}

    I should have been more clear on this point: this line will *always* force that has_many association to load its associated objects from the database, whether they’ve been previously loaded or not.

    This might cause a performance hit, depending on how you’re using this code. You could ask the proxy if it’s loaded,

    (user.purchases.loaded? ? user.purchases : user.purchases(:load)).target.sum {|p| p.price * p.quantity}

    but now the code is just getting awful.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *