Thursday, November 22, 2007

As part of my Rails learning, I've found a situation where I would like to be able to execute a method under a different controller's context. Something like:

def login_user   
    old_controller = @controller
    @controller = AccountController.new
    post :login, :login => 'patrick', :password => 'sekrit'

    @controller = old_controller
end

I'd like to write a helper method that accepts a controller type and a codeblock, then do something like (in pseudo-ruby)

def execute_under_different_controller controller, block
    old_controller = @controller
    @controller = controller
    block()
    @controller = old_controller
end

I don't think that it is the syntax, although I'm going to try to execute it. In order to do this, I need to figure out how to pass a codeblock to a method and execute it.

In order to figure this out, I need to write some tests. So, off I go to powershell to create a new gem for this. I had a set of tests before when I was learning some basic Ruby initially, inspired by Mike Clark's Ruby Learning Test series.

newgem learning_ruby --test=rspec

My first test looks like this

describe "Passing a codeblock" do
  it "should set a value" do
      @value = 0
    execute_this do
        @value = 1
    end
    @value.should == 1
  end

    def execute_this block
        block
    end
end

This is my belief that Ruby should really work the way that I think it should. Well, going to my command-line

spec spec

That didn't work. I got a "wrong number of arguments (0 for 1)" error. I guess it doesn't just pass the do blocks like I thought it should. Let's try this line:

execute_this { @value = 1 }

I believe that the {} notation is a stand-in for "do end," so this should give me the same result. Same result, so I'll leave it like this. The question is how to execute a passed in block. Off to "Programming Ruby." What do you know, it appears you just call yield. Let's try it:

def execute_this
    yield
end

And, it worked! Hurra. Now, I can get back to my Rails problem. I want to execute under a different controller, so I'll change the following method: 

def login_user   
    old_controller = @controller
    @controller = AccountController.new
    post :login, :login => 'patrick', :password => 'sekrit'

    @controller = old_controller
end

to

def login_user   
    execute_under_different_controller AccountController.new do
                            post :login, :login => 'patrick', :password => 'sekrit'
            end
end

def execute_under_different_controller controller
    old_controller = @controller
    @controller = controller
    yield
    @controller = old_controller
end

That worked. So, here's a question, could I just pass AccountController in and have the execute method call new? Let's try it.

def login_user   
    execute_under_different_controller AccountController do
                            post :login, :login => 'patrick', :password => 'sekrit'
            end
end

def execute_under_different_controller controller
    old_controller = @controller
    @controller = controller.new
    yield
    @controller = old_controller
end

That seems to have worked. So, I now have a nice helper method for Rails.
Thursday, November 22, 2007 11:47:01 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]