zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

attr_readonly

February 27, 2007 @ 10:48 AM by nap · 0 comments

I was really shocked yesterday to discover that there's literally no baked-in way to declare an ActiveRecord attribute as private or protected. Sometimes I don't want publicly-accessible ActiveRecord attribute methods. Sorry, but not every field in my database should have a corresponding public mutator.

Consider an IP address field on a User model, that's used to record the last IP the user logged in from. This is something that, although clients outside of the model should be able to access the value, they certainly shouldn't be able to set it. Only the model itself should be able to update that field in the database. It's private!

As another example, consider the case where a database field is just a cache of calculated relationship data. That cached value should never be set directly via a public instance method. One example is an average_rating as calculated from a bunch of user ratings on an article. Why would you want to expose a mechanism to set this directly? It should only be set through some sort of recalculate_average_rating public method that lives on your article model and fires as an after_save filter on a new rating object.

It turns out that countercache itself suffers from this same problem. Ticket #6896 in the Rails Trac points out this exact issue and proposes a solution in the form of attrreadonly. This is a solid solution, and would go a long ways towards enforcing proper encapsulation in AR.

I ranted about this yesterday in freenode and got yelled at a little bit. Yes, I was probably being a bit obnoxious, but I also don't want the importance of this to be overlooked. The bottom line is that, like it or not, people are largely stupid. Programmers are not excluded from this. Even Ruby programmers, who admittedly are more self-conscious than most. If you give someone the opportunity to do something stupid, be it at the user level in your app, within your API, or right within the rest of your code base, they'll do it. If we're writing for an audience > 1, we should be writing code like we design user interfaces -- the public methods available on our classes shouldn't include things that give our audience permission to twiddle with the guts of that thing. This is a cornerstone of encapsulation and good OO design.

So how do you feel about all this? Is it not A Big Deal to you? To me, it's a significant limiting factor when it comes to organizing large code bases amongst multiple programmers. If you're the only guy working on a hobby app, then fine, I guess you can police yourself. Go somewhere and do your thing. But if you're part of a larger team, or hope to build the foundation for something that can grow into a real enterprise-class app, then proper encapsulation is mandatory.

I really hope that patch gets committed, or that someone puts together a plugin that delivers the same functionality. In the meantime, sure, there are some things you can do that are better than nothing at all. Try declaring a public mutator in your class to override the AR attribute method of the same name and raise NoMethodError, or write a before_save filter that loads up the model into another variable and replaces the current field with it's previous value. But just describing those "solutions" makes me feel somewhat ill. Rails is such a clean, elegant platform, and it needs a clean, elegant way to provide non-public methods for AR attributes. Let's get that patch committed :-).

0 comments

Axiom Of The Empty Set

February 23, 2007 @ 06:02 PM by nap · 0 comments

A list without any items in it is still a list, I must insist. Just like a box without anything in it is still a box, or a Smurf village without any Smurfs (Smurves?) is still a frickin Smurf villiage..

But not according to the W3C, apparently. File this one under Rant Of The Day: none of the standard XHTML doctypes acknowledge the existance of the empty list.

<!ELEMENT ul (li)+>

That is, neither the <ul></ul> or the <ul/> representations are valid XHTML. This seems pretty broken to me at first glance, although I'm certianly willing to hear a rebuttal if anyone has one. Even if you don't think it's busted, it certainly adds complication to a very common web dev scenario...

So we have a list of resources in our view and there's a good possibility that our list is empty. We want to be able to display that list, and have a button on the page that lets me add new items to the list with a little Ajax love so that we don't have to reload the page. If we can represent an empty list in the page, it's simple: we can just render :update in Rails...

render :update do |page|
  page.insert_html(:bottom, :my_list, "<li>#{item}</li>")
end

The code above assumes that there's a DOM ID 'my_list' that has 0 or more elements. But since XHTML won't let us represent a 0-element list, the obvious thing to do is to bake some extra smarts into the update code.

Now it's the job of the update to determine if a list exists, and if not to create it, but only for the special one-off case of the zero element list? Yuck, bleh. No thanks. This is probably what most people do, but it just strikes me as putting too much intelligence into something that's supposed to be pretty dumb.

The alternate solution listed here also qualifies as a hack, no bones about it. But, at least in my opinion, it's a somewhat more elegant hack than what was described above. File under simple Workaround Of The Day:

<ul id="list_things">
  <li class="invisible" style="display: none"/>
</ul>

Note that the W3C does support empty list item elements :-). Thanks to azta for the suggestion.

0 comments

Super Mathematics

February 20, 2007 @ 10:05 AM by nap · 0 comments


Do the math. No matter what you think, nobody's perfect.

0 comments

NH Ruby UG Meeting.002 Tuesday

February 19, 2007 @ 09:10 AM by nap · 0 comments

If you're a Ruby developer (or just an interested outsider) living in southern Maine/NH or the northern Mass area, don't forget to attend the next meeting of the NH Ruby/Rails User Group. Discussion topic this week is RJS and Ajax. Come hang out with us in Portsmouth tomorrow and make sure to stick around afterwards for drinks, discussion, and merriment.

Oh yeah, and Scott has some free stuff to give away at the gathering too. You like free stuff, don't you?

0 comments

8.5 Minutes

February 17, 2007 @ 12:38 AM by nap · 0 comments

What were you doing for those eight and a half minutes? Was it mean, was it petty, or did you realize you were sorry And that you love them?

It’d be nice to think we could get it right down here just once. G*d bless the Plan.

0 comments

Inheritance vs Relational Databases in RoR

February 16, 2007 @ 05:33 PM by nap · 0 comments

There are three patterns in common use that deal with mapping object inheritance to relational databases. We didn't discuss any of them in my graduate databases course (sigh), and our friend ActiveRecord implements only one of them: Single Table Inheritance (STI).

STI is what you use when you want to represent an object hierarchy by mapping the union of all attributes found in that class hierarchy to a single underlying database table. Yup, it's a mouthful. And it works great if the attributes available on the subclasses all tend to be very similar. It's very fast (comparatively), but makes poor use of space since unused fields are just left null. ActiveRecord's implementation uses a type field in the database to identify the subclass that the row belongs to.

So what about alternative approaches? There are a couple... First up is Concrete Table Inheritance, in which there are n database tables for n concrete classes in the hierarchy. Although I'm sure there are cases where this must be useful, I can't think of a single one. The problem is the silly amount of replication that happens here -- fields common to classes will get replicated across tables. This means a lot of replication, more and more as the object hierarchy gets deeper, and a lot of difficulty when it gets to be refactoring time. Bleh, no thanks.

Class Table Inheritance, by contrast, has a table for each class, including interior nodes in the hierarchy (classes that have derivations). A subclass is represented at the database layer as a table that has some number of additive fields plus a foreign key to it's parent class, which contains the fields that are common to all ancestors.

Conceptually, this seems like the cleanest strategy to me, and the most "OO" of the approaches. However, when we think about the implementation, we realize that performance is going to be a big issue: the class-table strategy is going to require n joins for an object nested n levels into a class hierarchy. Ouch.

Despite that admittedly ugly problem, there has been some interest in implementing Class Table Inheritance in ActiveRecord. In fact there's some contributed code on the Rails wiki that should allow you to use it as an alternative to STI in Rails. Someone should plugin-ize it, perhaps.

I initially set out to look at alternative DB/object mappings because I was frustrated by the fact that STI wasn't "accommodating" enough for me. At one point I thought that Class Table Inheritance might be a better match for a specific problem we were looking to solve. It turns out that wasn't really the case at all; The problem wasn't STI, it was the way our object hierarchy was structured.

My Lesson Of The Day for February 16th, 2007: If your object hierarchy is relatively shallow, and the single table strategy produces ugly results for you (lots of null fields on subclasses), take another look at the relationships between your objects. STI is a great fit for things that are very closely related to the parent class and require few extra attributes to express that. It's a lousy fit, otoh, for object relations that aren't tight like rockstar pants. If both CarPart and DogLeash share the parent class Purchase, they're probably not good candidates for STI (wink, wink).

Embarrassed as I am to admit it, that was sort of the problem. More on that later, maybe.

0 comments

Going Solo: Resources Without The 'S'

February 12, 2007 @ 11:55 PM by nap · 0 comments

In case you missed it in the release announcement for Rails 1.2.2 last week, singular resources are now available in ActionController. You can use them to model singleton resources in your application.

So we're using technoweenie's restful_authentication plugin to handle all things authentication-related in our current project, and decided that a singular resource was a good fit for a Session (login, logout) and Account Controller.

Here's a dumbass-simple example:

map.resource :account, :member => { :activate => :any }

Note the missing 's'. No collections for us here, no sir. But what do we get?

GET /account => AccountController#show
GET /account/new => AccountController#new
POST /account => AccountController#create
GET /account;edit => AccountController#edit
PUT /account => AccountController#update
DELETE /account => AccountController#delete
(ANY) /account;activate => AccountController#activate

Hey, that's just what I needed. And of course we get the standard named routes for the resource like account_url, account_path, etc. Yay for Resources. Yay for REST.

0 comments

ActiveRecord Association Extensions

February 07, 2007 @ 09:57 PM by nap · 0 comments

So this is probably old hat to a lot of you, but for those that don't know, ActiveRecord Association Extensions are a seriously useful widget to have in your Batman Fantasy Camp utility belt.

You've no doubt noticed that when you define an association between models, you get a bunch of nifty methods on the resultant collection like push, delete, count, uniq, and so on. But what if you want some bit of functionality defined on an association that isn't already baked in?

Why we can extend our associations to define our own methods, of course. Let's see how...

Okay, so we have a relationship defined between two models: articles and opinions. An article has_many opinions. An opinion has a score on it, which is an integer between 1 and 10 representing how a user felt about the article.

Using Association Extensions we can define our own method on the association itself, called total_score:

class Article < ActiveRecord::Base
  has_many :opinions do
    def total_score
      sum(:score)
    end
  end
end

Now you can access this information by writing something like:

article.opinions.total_score

Damn, that's intuitive. Maybe we want to add another method on the association to retrieve the average opinion of users who voted on this article today:

has_many :opinions do
  def average_opinion_today
    find(:all, :conditions => ["created_at >= current_date()"]).average
  end
end

article.opinions.average_opinion_today

And it gets better. If we find ourselves using the same extensions in more than one place, in true DRY spirit we can build a module and reuse it. When we use the :extend option in an association, we get all the methods in that module mixed in.

module MiscellaneousExtensions
  def average_opinion_today
    find(:all, :condition => ["created_at >= current_date()"]).average
  end

  def total_score
    sum(:score)
  end
end

class Article < ActiveRecord::Base
  has_many :opinions, :extend => MiscellaneousExtensions
end

I put this module in a subdirectory of lib called extensions. You'll have to add that path to the config.load_paths in environment.rb in order for it to be recognized.

Using these sorts of extensions on my own project earlier today really helped me clean up some troublesome model code. I was just so amped up about it that I had to share.

For more information, see the ActiveRecord::Associations API Reference. Oh and a big shout out to Brian Hogan for pointing me in the right direction.

0 comments