Thursday, November 22, 2007

As I wrote about previously, I needed to have a logged on user when doing my tests of the StoryController. With a little help from google and "Programming Ruby," I found a way to send the login action to a different controller. This mostly was because I couldn't figure out how to set a session value (session[:user_id]).

Just a couple pages later in the book, there are two methods

def get_with_user(action, parameters=nil, session=nil, flash=nil)
    get action, parameters, :user_id => users(:patrick).id
end

def post_with_user(action, parameters=nil, session=nil, flash=nil)
    post action, parameters, :user_id=>users(:patrick).id
end

Well, those are slightly easier. It looks like I know how to set session values on post and get, so I now have learned two things today: how to pass a code block and execute it; and, posting/getting with session variables set.

Sweet!

Going a few pages later, it has the following paragraph:

"If you've been particularly eager, and tried executing your functional test suite prematurely, you'll have noticed that a few tests that worked previously now fail. These failures occur because we modified our story submission form - it requires that a user_id is present in the session before a page request can be successful. Our old tests didn't account for this change, so they now fail."

HAHA! It has been ingrained in me that all my tests should pass always before I move on to something new has failed me here; I didn't follow the directions of the book word-for-word. If I had just been satisfied with typing the stuff from the book and following directions, I wouldn't have had spent time figuring out how to get the tests to pass. :) In the end, though, I learned a couple things about Ruby and Rails, so I'm happy.

Thursday, November 22, 2007 12:14:15 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

Interesting. The book I'm going through for rails has added login functionality to adding a new story to the site. I've added a before_filter for the new action on StoryController.

before_filter :login_required, :only => :new

Now, though, my StoryController tests fail that look at getting the new form. The :login_required filter looks like

def logged_in?
    ! @current_user.blank?
end
helper_method :logged_in?
def login_required
    return true if logged_in?
    session[:return_to] = request.request_uri
    redirect_to :controller => "/account", :action => "login" and
        return false
end

Well, unfortunately, in order to get a user logged in, I need to call the :login action on the AccountController. The book didn't include this, so my functional tests for StoryController are failing, as a test like

def test_should_show_new_form
    get :new
    assert_select 'form p', :count => 3
end

doesn't have form with 3 p elements. I had a thought that it was getting redirected, so I changed the test to

def test_should_show_new_form
    get :new
    assert_redirected_to :controller=>"/account", :action=>'login'
end

and it passes. So, it appears that the before_filter is working.

So, I need to fake out the filter somehow; I need to set up a logged on user. The log on looks like:

before_filter :fetch_logged_in_user
protected
def fetch_logged_in_user
    return if session[:user_id].blank?
    @current_user = User.find_by_id(session[:user_id])
end

I am not 100% sure if I need to fake the session[:user_id] or just have a @current_user. I tried both, actually, and

session[:user_id] = 1

throws an exception in the test.

@current_user = users(:corey)

doesn't work either.

I figured that I could call the login action with credentials from users(:corey). I first tried

post :controller=>'/account',:action=>'login', :login => users(:corey).login, :password => users(:corey).password

at the top of the my test, hoping that it would call the action and get my system logged in. Here's the exception I got:

ActionController::UnknownAction: No action responded to passwordCorey Hainesactionlogincontroller/accountlogincorey

That is a really strange error. Looking at it, I think my understanding of the users fixture may not be getting what I think, so I'll try this:

post :controller=>'/account',:action=>'login', :login => 'patrick', :password => 'sekrit'

Nope, that didn't work either. So, my I do understand users. Looking closer at the exception, I'm guessing that I may just have the parameters for post wrong.

Off I go to http://api.rubyonrails.com/ to look up post.

post(path, parameters=nil,headers=nil)

Doesn't look like the post that I'm using. Doh! Of course it isn't, since I'm using the post from the testing framework. Looking up on google, I finally find a post called "#post method in tests with a different controller."

It turns out that you need to redefine the controller, then call post, then reset the controller. Let's try it. I changed the test to the following code:

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

    @controller = old_controller
    get :new
    assert_select 'form p', :count => 3
end

I ran the test, and, voila, it worked! So, now, I just need to add this to the other tests that are failing. I have a test to check adding a story:

def test_should_add_story
    post :new, :story => {
        :name=>'test story',
        :link=>'http://www.test.com/'
    }
    assert ! assigns(:story).new_record?
    assert_redirected_to :action=>'index'
    assert_not_nil flash[:notice]
end

Let me change it to support logging in:

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

    post :new, :story => {
        :name=>'test story',
        :link=>'http://www.test.com/'
    }
    assert ! assigns(:story).new_record?
    assert_redirected_to :action=>'index'
    assert_not_nil flash[:notice]
end

HUZZAH! It worked! Let me pull it out into a new method:

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

    @controller = old_controller
end

This seems like a fantastic helper method execute_under_different_controller(controller, code). I'm not 100% sure how to build this method, so I'm going to take a minute to learn how to do it in Ruby.

We now have a nice method called execute_different_controller, so changed login_user to

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

I don't like the fact that I'm duplicating the data from my users.yaml file in my tests. If I load the fixture, I should be able to reference users(:patrick) here. Let's try it.

def login_user   
    user = users(:patrick)
    execute_under_different_controller AccountController do
                            post :login, :login => user.login, :password => user.password
            end
end

 Well, that worked. I guess I can understand that the book doesn't push too hard on this point, but it is a crazy, dangerous amount of duplication in your tests. This sort of data duplication leads to fragile tests.

After adding login_user to the tests for StoryController, things are back on track to continue through the book.

Thursday, November 22, 2007 12:04:21 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

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]
 Monday, November 19, 2007

Well, I'm having a great time so far working through the Ruby on Rails book that I downloaded. It is really fun, except that Ruby runs like a dog on my machine. It literally takes 10 seconds to run my test suite; it appears that it takes most of the time just to read the files. I disabled my virus scan, but it doesn't seem to have helped. UGH!

Monday, November 19, 2007 7:31:08 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Saturday, November 17, 2007

Well, I have been using Notepad++, but I am going to try UltraEdit for this book. I then might switch back to trying out e.

(I followed this script on adding RoR support for UltraEdit. I'll see how it works)

Saturday, November 17, 2007 3:59:54 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

Well, I have officially started on my Ruby On Rails (RoR) learning experience. I downloaded a free RoR PDF from sitepoint and am working my way through it.

So far, I have set up MySql (after going through too much figuring out that the password for root was somehow set to sa), installed rails, and I have now done the following stuff in the ruby console:

  • Create a Stories table in MySql (used the mysql console for this)
  • Created a Story class definition in ruby, subclassing ActiveRecord::Base
  • Put the following line of code:
    story = Story.new
    story.save

and, voila, it showed up in the database. Sweet! I'm going to try to keep blogging my thoughts as I go through it.

Saturday, November 17, 2007 3:56:55 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

I recorded and watched the episode of Nova about the Dover, PA, creationism in high school case. I watched it on Thursday, and I highly recommend it to everyone.

[Update: It is now available to view online]

Saturday, November 17, 2007 10:18:14 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
What Kind of Reader Are You?
Your Result: Literate Good Citizen

You read to inform or entertain yourself, but you're not nerdy about it. You've read most major classics (in school) and you have a favorite genre or two.

Dedicated Reader
Obsessive-Compulsive Bookworm
Book Snob
Fad Reader
Non-Reader
What Kind of Reader Are You?
Create Your Own Quiz
(courtesy of sorting out science)
Saturday, November 17, 2007 9:50:03 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Saturday, November 10, 2007

Nova is airing a show on the Dover, PA, ID case on intelligent design in the classroom. Nova generally does good shows, so this will be one to tape. Here's a link to the local listings for your area.

Saturday, November 10, 2007 6:27:51 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

Fraser Cain mentions that the Rosetta spacecraft is going to be making a close encounter with Earth on November 13, 2007. It is doing a fly-by to pick up some speed on the way to Comet 67/P Churyumov-Gerasimenko. Fraser writes a good explanation of gravity assist and why Rosetta is flying back by Earth if the comet is out in space. If you don't know how spaceships can use gravity to speed up, go read it. After that, say hello to Rosetta on November 13.

Saturday, November 10, 2007 4:49:11 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

The Bad Astronomy blog has a cool link to a picture from Earth Science Picture of the Day that shows the paths of solar eclipses over the last two thousand years.

Today's Earth Science Picture of the Day.

 

 You can click on the picture to get a larger view; it is much more impressive big, so do it!

Also, he mentions that the next total solar eclipse is August 1st, 2008 and starts in the far north of Canada. The Agile 2008 conference is in Toronto and starts on August 4th. Hmmmm... Wouldn't that be a sweet trip: go see the beginnings of the solar eclipse in way northern Canada, then travel down to Toronto for the conference. Probably won't happen for me, but still a cool dream.

Saturday, November 10, 2007 2:34:04 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Wednesday, November 07, 2007

Sarah Rainsberger sent me a link to a great picture from the Agile 2007 conference that is posted on Willem van den Ende's blog. I'll admit that I don't know Willem, although his picture sure does look familiar (I must have met him at the conference).

I remember exactly when the picture was taken (although I don't remember the actual camera). I was sitting on the couch, doing some stuff in Ruby that I wasn't totally sure how to do, and, as happens at the Agile conferences, it only took about 2-3 minutes before someone comes over (in this case, it was Joe Rainsberger) and says, "hey, what are you working on?" He knows Ruby better than I do (I'm very much at the "still a beginner" stage), so he sat and we started looking at what I wanted to do. Next thing I knew, we had a few people looking at it. Good times.

At the Denver conference in 2005, when Joe won the Gordon Pask award, he said that the Agile conference had become for him a sort of family reunion. Since then, I've quoted him often when talking about the feeling that I get during the week. I love the first day, before the sessions have started, when everyone sees each other again (a lot of people only see each other this one time a year) and gets caught up. For example, I distinctly remember standing in the lobby of the hotel this year (I actually have a picture in my mind of exactly where I was standing), when up walked the other Agile Cor(e)y (he doesn't have an e). I hadn't really spoken to him since the previous year, so it was great to catch up in person. I actually don't spend a tremendous amount of time in the sessions at the conference, as I get a lot more out of the side conversations, where I can share experiences with people.

A friend of mine grew up going to camp every summer, and she speaks about it very fondly; it really does seem like it is a huge part of her life. I've mentioned to her that the Agile conference is like my camp. I proudly have said to people that it is one of the personal and professional highlights of my year. I go to my share of other conferences, such as Codemash, SDTConf and XPDay, but I do love the Agile conference for the opportunity to spend a week with a fantastic group of people.

Wednesday, November 07, 2007 3:18:23 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Monday, November 05, 2007

 

I'm surprised I don't see more people having this on their wall. After all, there are so many who do believe that the bible contains absolute truth. Perhaps the answers in genesis people can start selling these.

Monday, November 05, 2007 3:54:57 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Saturday, November 03, 2007

Mary makes a great decepticon, eh?

 

Saturday, November 03, 2007 9:11:25 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

Wow! I sure make a great autobot!

 

Saturday, November 03, 2007 9:07:48 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]