Rails


I’ve started using test/spec as a replacement for SimplyBDD. It’s great, except for one small thing: I can’t specify the superclass for the test case classes, which basically rules out integration tests in Rails. Rather than rewrite all my integration tests to use the standard test/unit syntax, I decided to patch test/spec. The patch can be found here.

Ruby is a great language. Really. How many other languages let you extend other classes? Modify their behavior? Overload operators? With great power comes great opportunity, and in this post I’ll cover one of my recent extensions to Ruby that was recently accepted into Rails’ core.

Date and Time

Managing dates and times is not exactly fun in any language. There are time zones, leap years, two meridians, twenty-four hours, 365 days (sometimes), and a mess of inconsistently long months. Those of us who use Ruby frequently are familiar with how to get objects corresponding to the current time and the current day:

  1. >> Time.now
  2. => Thu Jan 25 20:12:50 -0800 2007
  3. >> Date.today
  4. => #<Date: 4908251/2,0,2299161T>

Sexy methods…

Rails has these really cool extensions to Numeric that allow computing lengths of time in a more human-readable way. For example, if I wanted to figure out how long 12 hours is, I could just call 12.hours and get back the number of seconds in 12 hours, which could then be conveniently added to an instance of Time, like so:

  1. >> t = Time.now
  2. => Thu Jan 25 19:31:17 -0800 2007
  3. >> t + 12.hours
  4. => Fri Jan 26 07:31:17 -0800 2007

Sweet! What’s even cooler though is that, in addition to Numeric with seconds, minutes, hours, days, weeks, fortnights, months, and years, we also get from_now, ago, since, and until:

  1. >> 1.day.from_now
  2. => Fri Jan 26 19:34:20 -0800 2007
  3. >> 2.weeks.ago
  4. => Thu Jan 11 19:34:35 -0800 2007

but kinda dumb.

Nice! These are so easy to read. These were great for simple things, but since they returned numbers, they couldn’t take into account the current date and how many days were in a month, leap year, etc. Notice that this causes problems when the month doesn’t have 30 days in it:

  1. >> Time.now
  2. => Thu Jan 25 21:01:31 -0800 2007
  3. >> 1.month.from_now
  4. => Sun Feb 24 21:01:34 -0800 2007

So it’s good for approximations, but not much else. Well what now?

A smart method

To compensate for the lack of accuracy in the above sexy methods, Time#advance was added to make it easy to do smart addition to Time instances. Here’s some of the above using advance:

  1. >> t = Time.now
  2. => Thu Jan 25 21:11:32 -0800 2007
  3. >> t.advance(:hours => 12)
  4. => Thu Jan 25 21:11:32 -0800 2007
  5. >> t.advance(:months => 1)
  6. => Sun Feb 25 21:11:32 -0800 2007

Excellent! Not quite as sexy, but definitely smart. A method suitable for Serious Business.

Smart n’ Sexy

I welcomed Time#advance, like everyone else, but I didn’t want to give up the sexy methods. Fortunately, Ruby is great for letting objects masquerade as other objects (the whole duck typing thing), and the de-facto way to do this in Rails is by using Builder::BlankSlate, whose instance undefine almost all of their methods, allowing you to easily proxy all or some methods to something else. The two best examples of its use in Rails are AssociationProxy, which is used by Active Record, and JavaScriptGenerator, which is used by RJS.

To solve the problem, all we need to do is make the duration methods on Numeric return a proxy for the number they used to return which acts accordingly around Time and Date objects. This new class is located in ActiveSupport::Duration, and simply accumulates lengths of time for when it is used around a Time or Date, and a number for use around things that expect it to act like a number (i.e. for backward compatibility). Here’s the new, smart and sexy, methods on Numeric:

  1. >> t = Time.now
  2. => Thu Jan 25 21:21:44 -0800 2007
  3. >> t + 1.month
  4. => Sun Feb 25 21:21:44 -0800 2007
  5. >> t + 1.week
  6. => Thu Feb 01 21:21:44 -0800 2007
  7. >> 1.year.from_now
  8. => Fri Jan 25 21:22:00 -0800 2008
  9. >> 4.years.from_now
  10. => Tue Jan 25 21:22:12 -0800 2011
  11. >> 3.weeks
  12. => 21 days

How’s that last one for overriding inspect? To try it out all you have to do is freeze edge in a Rails project near you!

$ rake rails:freeze:edge

Update: I should add that this recent change also corrected handling around Date objects. I implied that above, but I should be explicit. Before Duration, adding Date.today and 1.day didn’t yield the expected results at all:

  1.  
  2. >> (Date.today + 1.day).to_time
  3. ArgumentError: time out of range
  4.  

Now it behaves as expected:

  1.  
  2. >> (Date.today + 1.day).to_time
  3. => Sat Jan 27 00:00:00 -0800 2007
  4.  

Earlier I mentioned the contest that CD Baby is putting on in cooperation with WorkingWithRails. I’d hoped that my Duration patch (which was accepted) would be enough to keep me in the running until the 22nd. By chance I also ran into a problem with the PostgreSQL adapter while working, and took some time to fix it. That was actually accepted just a few hours after I wrote it and made it into 1.2, which was recently released.

I think it’s great how much activity this contest has spurred. There are many, many documentation patches now. People have been using Kevin Clark’s Heckle to find gaps in Rails’ test suite and writing patches to fill those gaps. People’s little pet peeves are now being submitted as patches (mine included). The downside is that the core guys now have a lot more work to do, but now that Rails 1.2 is out they’ve got a little more time.

So thanks to everyone on this list - those who have contributed to Rails within the last 20 days or so. Rails has a pretty amazing community and it’s great to work with such talented, devoted people. I’m sure the activity will die down a little after the 22nd, but at least for me it’s helped me feel a bit more a part of the community, and I bet others feel the same. Hope to see you all at RailsConf!

At the Ruby meetup Josh Susser mentioned that CD Baby, a Portland-based Rails shop, has purchased 20 tickets to RailsConf ‘07 as well as 0 hotel rooms in the area (Portland). These will go to the top 20 contributors from Jan 1 to Jan 22. I checked the list and found that, as of yesterday, I’m in the top 20. Hopefully my Duration patch will be accepted and I’ll stay in the top 20 until the 22nd. Any suggestions?

For anyone as annoyed as I am about 1.month.from_now being inaccurate, lean on your favorite Rails team member to get this patch accepted.

When coding there are many guidelines you might opt to follow, such as Convention Over Configuration, the Principle of Least Surprise, and others, all intended to prevent you from falling into certain pitfalls. One such pitfall is one that I myself often fall victim to, and involves spreading the knowledge of the internal workings of one component to several others. That is, if A, B, and C all know how D works, then there’s really little point in grouping functionality inside D in the first place. Avoiding this pitfall is what the Law of Demeter tells us to do. To quote the Wikipedia article:

When applied to object-oriented programs, the Law of Demeter can be more precisely called the “Law of Demeter for Functions/Methods” (LoD-F). In this case, an object A can request a service (call a method) of an object instance B, but object A cannot “reach through” object B to access yet another object to request its services. Doing so would mean that object A implicitly requires greater knowledge of object B’s internal structure. Instead, B’s class should be modified if necessary so that object A can simply make the request directly of object B, and then let object B propagate the request to any relevant subcomponents. If the law is followed, only object B knows its internal structure.

Unfortunately this is done all the time in Rails, partly because they make it so darn easy to access associations and their associations:

  1.  
  2. shipment.user.profile.mailing_address
  3.  

Looks innocent enough. But what happens when we sprinkle this around our codebase in a variety of forms, and then without warning the requirements change and all of a sudden the mailing address is now attached to the user rather than the profile. This will require going through all your code that might look for the mailing address and updating it. You’d better pray you have near 100% test coverage.

The unpredictable and despotic need for change that creeps into every project is the reason you should care about the Law of Demeter, also called the Principle of Least Knowledge. But what do we do about it? The Wikipedia excerpt above makes it pretty clear that we should define mailing_address on Shipment and User:

  1.  
  2. class Shipment < ActiveRecord::Base
  3.   …
  4.   def mailing_address
  5.     user.mailing_address
  6.   end
  7.   …
  8. end
  9.  
  10. class User < ActiveRecord::Base
  11.   …
  12.   def mailing_address
  13.     profile.mailing_address
  14.   end
  15.   …
  16. end
  17.  

Okay, fine. Not the prettiest, but it does help us with refactoring. You may have noticed a problem with both the old code and the new version: what if one of the associations is nil? We’ll get a big fat NoMethodError of course! Let’s fix that:

  1.  
  2. class Shipment < ActiveRecord::Base
  3.   …
  4.   def mailing_address
  5.     user ? user.mailing_address : nil
  6.   end
  7.   …
  8. end
  9.  
  10. class User < ActiveRecord::Base
  11.   …
  12.   def mailing_address
  13.     profile ? profile.mailing_address : nil
  14.   end
  15.   …
  16. end
  17.  

Getting kinda ugly, but it works better now. Both refactoring and nil problems are taken care of. Now that we’ve got that, we can rip it out. A while back Rails got a method called delegate that’ll let us do just this type of thing, providing both the refactoring safety and nil safety. Using this method we can change our code to this:

  1.  
  2. class Shipment < ActiveRecord::Base
  3.   …
  4.   delegate :mailing_address, :to => :user
  5.   …
  6. end
  7.  
  8. class User < ActiveRecord::Base
  9.   …
  10.   delegate :mailing_address, :to => :profile
  11.   …
  12. end
  13.  

Isn’t that cool? Now it’s nice and semantic, safe, and refactorable!

While it’s nothing major, Rails recently got the ability to render JSON a little more easily:

  1. render :json => items.to_json

This sets the content type to “application/json” and renders whatever text passed. But wait! There’s more! You can also start implementing Yahoo!-esque REST web services with JS callbacks like so:

  1. class PeopleController < ActionController::Base
  2.  
  3. def show
  4. @person = Person.find(params[:id])
  5. respond_to do |format|
  6. format.html
  7. format.xml  { render :x ml => @person.to_xml }
  8. # this can be called with /people/1.json?callback=loadPerson
  9. # and will generate ‘loadPerson({name: "Brian", skills: "bowhunting, nunchuck, computer hacking"})’
  10. format.json { render :json => @person.to_json, :callback => params[:callback] }
  11. end
  12. end
  13.  
  14. end

You might wonder why I’d bother blogging about this. It’s because this marks my 5th patch into Rails (well, maybe 4.5 since it was originally posted by someone else and I updated it with tests). w00t!

In my continuing efforts to keep abreast of the latest and greatest software, I decided to evaluate switching from WordPress to Mephisto. The former is great, as it’s been around a long time, has a lot of plugins, and Just Works. However, if I want to dig into my blog at all, I have to do it in PHP, which is a bit like Spanish for me: I can read it well enough, but I can’t write it without constantly picking up a reference.

What does this have to do with SVK? Well, SVK was mentioned along with Piston as a way to manage syncing one repository with another. I already use and love Piston, so I decided that anything mentioned alongside it is probably worth checking out. And so it seems to be. SVK is apparently a client-side replacement for the normal svn command. It brings in a feature from Darcs that I’ve found myself wishing for from time to time: full local repository access with local commits.

It’s yet to be seen whether anything has to be done server-side for this to work, but from what I’ve read, I highly doubt it. If that’s the case it’ll be a godsend when I’m on vacation as I’ll be able to commit, branch, roll back, and do all those other nifty tricks that Subversion has taught us to do instead of only being allowed svn revert. It’s installing (with some difficulty) with MacPorts as I type this. I’ll post more when I know more.

UPDATE: Okay, got it synced with Attendio and tried a commit. I’m sold.

We’re using FK constraints at Attendio. As you all know, this means we must be careful about the order we load fixtures in our tests when calling fixtures. But what if you can’t control the order of the fixture load as when using the FixtureSets or FixtureScenarios plugins? Or maybe you simply don’t want to have to worry about the order?

One solution is to disable FK constraints before loading the fixtures, then enable them afterward. This is liable to be a vendor-specific solution, however, and it just feels bad. Another solution is to determine the correct order and ensure that the fixtures you want to load are loaded according to that order (which would require a modification to the plugins, but at least they could then work without the user needing to specify an order). Let’s say you have a few models:

  1.  
  2. class User < ActiveRecord::Base
  3.   has_many :memberships
  4.   has_many :groups, :through => :memberships
  5.   has_many :taggings, :as => :taggable
  6. end
  7.  
  8. class Group < ActiveRecord::Base
  9.   has_many :memberships
  10.   has_many :users, :through => :memberships
  11.   has_many :taggings, :as => :taggable
  12. end
  13.  
  14. class Membership < ActiveRecord::Base
  15.   belongs_to :user
  16.   belongs_to :group
  17. end
  18.  
  19. class Tag < ActiveRecord::Base
  20.   has_many :taggings
  21. end
  22.  
  23. class Tagging < ActiveRecord::Base
  24.   belongs_to :tag
  25.   belongs_to :taggable, :polymorphic => true
  26. end
  27.  

One correct order for loading the fixtures is users, groups, memberships, tags, taggings (there are four correct ways to load them). All we really have to do is look at the non-polymorphic belongs_to associations, and we’re set. Here’s some code that does just that.

Update: the code is cleaner now, and probably faster since it doesn’t build up an entire dependency tree.

  1.  
  2. def load_order
  3.   models = Dir[RAILS_ROOT + ‘/app/models/**/*.rb]
  4.   models.each { |file| require(file) }
  5.  
  6.   klasses = models.map { |file| File.basename(file, ‘.rb).camelize.constantize }
  7.   klasses.reject! { |klass| !klass.ancestors.include?(ActiveRecord::Base) }
  8.  
  9.   deps = klasses.inject({}) do |h,klass|
  10.     h[klass] = klass.reflect_on_all_associations.select { |a| a.macro == :belongs_to && !a.options[:polymorphic] }
  11.     h[klass] = h[klass].map { |a| a.class_name.constantize }
  12.     h
  13.   end
  14.  
  15.   order = []
  16.   until deps.empty?
  17.     free = deps.keys.select { |k| deps[k] == [] }
  18.     deps = deps.without(*free)
  19.     deps.each { |k,v| deps[k] = v - free }
  20.     order += free
  21.   end
  22.   order.map(&:table_name)
  23. end
  24.  

The code is rough and not yet integrated in a useful fashion, but it gives you an idea and will let you integrate it into your fixture solution as I have. It won’t work with HABTM relationships since there’s no model for the join table. Other than that it seems decent, if a bit on the ugly side. If anyone wants to clean it up I’d be obliged. Oh and by the way it’s under the Creative Commons Share-Alike license.

“Putting Rails to Work” - November Ruby Meetup

Begins: Tue, 14 Nov 2006 at 6:00 PM
Ends: Tue, 14 Nov 2006 at 7:00 PM
Entry fee: Free
Location: Odeo
164 South Park St.
San Francisco, CA 94107
Although Bosco will be away, Josh Susser is organizing this baby and he has something up his sleeve. It’s going be real good. Stay tuned…
Tags: ruby rails

Next Page »