iBrasten

My methods of calculating time are far superior to yours, in every way.

 

This is the blog of Brasten Sager, a software engineer, Mariners fan, guitarist, haphazard philosopher.

Edge report: with_scope protected

June 19, 2007 @ 12:59 PM

I knew all along that using with_scope outside my models was a bad idea, but I did it anyway. So once this little change came through, I had to rethink some of my code…

Just to throw this out there – let’s call it peer-review – my new method of encapsulating with_scope usages sort of piggy-backs on finder encapsulation.

Take, for example, the following code:

1
2
3
4
5
6
7
8
9
10
11
12

class Post < ActiveRecord::Base
  def self.find_active
    # ... some finder code to return all active posts
  end
end

class PostsController < ApplicationController
  def index
    @posts = Post::find_active
  end
end

… despite my habit of encapsulating finders, I still found myself doing this:

1
2
3
4
5
  
# In around-filter or action
Post::with_scope( ... conditions ... ) do
  # ... yield or something
end

– Obviously, BAD. So the alternative is to do something like Post::scope_by_active or something. What about adding a fairly simple block_given? condition to the existing find_active method? Use your imagination on the implementation of that, but the usage would now look something like this:

1
2
3
4
5
6
7
8
  
# No block, returns all active
@posts = Post::find_active

# block, scopes to active, returns result of block.
@my_posts = Post::find_active do
              Post::find_by_user( user )
            end

Soooo… peer-review away!

CBS revives Jericho!

June 06, 2007 @ 03:53 PM

Impressive campaign – I put together a little Yahoo Maps illustration with the location and amounts of most of the contributions to NutsOnline.com’s Save Jericho campaign. The live version can be seen here.

Well done everyone!

So, if you’ve tried RSpec, you know it let’s you write test code that’s easy to understand. There are other reasons for BDD, but that is probably the first one you’ll notice. Describe this, it should do that, this should not have five of those, etc etc. It’s great for less assertive people like myself.

Occasionally, you’ll run into situations that are not easily described with the stock RSpec API. The RSpec guys have done a fantastic job putting together an API that is easy to manipulate, so don’t be afraid to do that!

Let’s look at an example:

1
2
3
4
5
6
7
8
9

describe TicketsController, "with unauthenticated visitor" do
  it "should require login to access /index" do
    get 'index'

    # This is the line I'll be picking on.
    response.should redirect_to( :controller => 'account', :action => 'login' )
  end
end

Pretty basic, right? But here’s the part that bugs me: it “should require login to access /index” vs. response.should redirect_to( :controller => ‘account’, :action => ‘login’ ).

The expectation I’ve described in english is not the same as the expectation I’ve described in code. Redirecting to a login page is a function of the before_filter handling your app’s security, not of the TicketsController itself.

In english, my expectation is pretty clear: ‘it should require login’. So let’s make the code match.

1
2
3
4
5
6
7
8
9

describe TicketsController, "with unauthenticated visitor" do
  it "should require login to access /index" do
    get 'index'

    # Doesn't this make a lot more sense??
    response.should require_login
  end
end

Turns out, this is shockingly easy to accomplish:

1
2
3
4
5
6
7

# Add this to your spec_helper.rb or another required file
module Spec::Rails::Matchers
  def require_login
    RedirectTo.new(request, { :controller => 'account', :action => 'login' })
  end
end

And there you go. Requiring a login is now an expectation on the same level as all others. It’s really very cool!

Additionally, this ALMOST gets us the negative expectation ( “response.should_not require_login” ). There’s a very minor tweak needed to get that working, but that’s an exercise I’ll leave to you.

If you don’t want to figure it out, I can be bribed with gift cards to Caffe Ladro for as little as $5. =)