Wednesday, September 10, 2008

Installing the Sphinx search engine and thinking_sphinx/mysql gems on Mac OSX with XAMPP

For development, I use XAMPP for Mac OSX to run MySQL. Since XAMPP packages the binaries and library files in a virtual environment under /Applications/xampp, this presents some problems when compiling programs that depend on these MySQL files.

One particular example is when trying to install the Sphinx search engine or the mysql rubygem. The following are instructions on how to install these applications. I'm assuming that XAMPP is installed in the default directory: /Applications/xampp.

First of all, the mysql rubygem can be installed by running:

sudo gem install mysql -- --with-mysql-dir=/Applications/xampp/xamppfiles/ --with-mysql-include=/usr/local/mysql/include


And that should install the gem without any problems.

In order to install Sphinx, you'll need to compile it from source by following these instructions:

1. Visit http://www.sphinxsearch.com/downloads.html and save the latest version (0.9.8 as of this post) of the Sphinx source somewhere on your computer .

2. Run "tar xvzf sphinx-0.9.8.tar.gz" to unpack the archive, and cd into it.

3. Execute:
./configure --with-mysql=/Applications/xampp/xamppfiles/ --with-mysql-includes=/usr/local/mysql/include


4. Then run "make" and "sudo make install"


If you receive an error about expat or iconv during make, you'll need to follow these instructions to compile and install those programs from source.

At this point, you should be able to run "sudo gem install thinking_sphinx" and have it install without a hitch.

There you have it! Sphinx and the thinking_sphinx/mysql gems installed and working with XAMPP.

Monday, August 25, 2008

Rspec format options

Although I use autotest and Textmate (CMD+R is second nature now) when I'm writing Rspec tests, once in a while I'll run a good old 'rake spec' from the console just before I do a git commit. The default behaviour for Rspec is to show the progression of tests as green dots, or F's (see 'progress' example below), but there are many more options as well.

Rspec has many different output formats to choose from, however you can only define one format to be printed to the console. If you want any more formats to be printed, you'll have to output them to a file (explained next).

To modify your Rspec options, you need to edit your RAILS_ROOT/spec/spec.opts file. Here's one of my files to show as an example:

--colour
--format progress
--format specdoc:spec/spec_full_report.txt
--format failing_examples:spec/spec_failing_examples.txt
--format html:spec/spec_report.html
--loadby mtime
--reverse

As you can see here, there are 4 formats defined; the one that's being printed to the console is 'progress', and specdoc, failing_examples (outputs the name of the failing examples), and html are all being outputted to separate files in the spec folder. Just follow the "format:file_output_name" convention and everything should work. Here are some examples of the different formats.

'progress' - default output format (--format progress):


'specdoc' - detailed output format (--format specdoc):


'html' - HTML output format / Textmate (--format html:spec/spec_report.html):

Sunday, August 17, 2008

Fork of restful-authentication to authenticate with email instead of login names

I have started a new personal project, and one of the first things I implemented was a user authentication system. I looked into restful-authentication and found that it accomplished everything I needed, except that it required a login name for authenticating.

It has always annoyed me when a site required that I give a valid e-mail address and provide a username, which was usually taken, and then having to append an arbitrary string at the end just to make it unique. Making a registration system based on an e-mail address and password, as well as an optional full name field, just seemed to make more sense to me.

So, instead of creating a full fledged user authentication system, I decided to fork technoweenie's very well made restful-authentication plugin, and have it use e-mail addresses for the login instead of login names. I also modified the rspec tests to account for this change, and now they all pass. You can find the github page here.

All credit goes to technoweenie, of course. I merely refactored the tests and plugin functionality to account for a small change. All original documentation still applies, so check it out if you're in the market for a new user auth system.

Monday, August 11, 2008

Using rspec to test actionwebservice with Rails 2

Background:
I'm currently working on an application for a client who requested that web services being served by the app are in WSDL format. In Ruby on Rails (before REST became the de facto standard), this was accomplished by using Actionwebservice, but unfortunately it was removed from the Rails core as of version 2. Luckily, it's still available as a gem, and can be reintegrated with your RoR application by following these instructions: Using Action Web Service with rails 2.0

Problem:
After I got AWS working with my application, the problem of testing these methods arose. Even before AWS was ousted from the Rails core, documentation on AWS testing was sparse and any available material usually ended up referring to this RDOC. The RDOC indicates that testing AWS methods should be similar to functional tests for controllers.

While this may have worked, I wanted to test my web service methods using rspec and Mocha for mocking the model behaviour (instead of having to use fixtures), and also to decouple the controller tests from the model layer.

Solution :)
I'm assuming at this point that the rspec testing framework is working properly with your RoR application, and that you're familiar on how to generate regular controller tests with it.

As a side note, I'm using Mocha for mocking data, which can be installed with "gem install mocha" and adding "config.mock_with :mocha" in your spec_helper.rb file.

Finally, to enable rspec tests to work with your AWS controller, add the following line to the top of your *_controller_spec.rb file:
require 'action_web_service/test_invoke'


With these preparations completed, we can move onto the actual tests. As an example, I had the following authentication method to test:

def authenticate(username, password)
# Authenticate user
@user = User.try_to_login(username, password)

if @user
return @user
else
return nil
end
end

(I removed some code in this method for the purposes of this example, so I'm aware that this could have been done in one line without the need for the if statement)

Where User.try_to_login would return a User object if the login was valid, or nil otherwise. Now in order to test this, we need to make sure that the model logic is clearly separate from the controller logic. This means that User.try_to_login should not actually hit the User model, and instead be mocked in the test.

The following is my rspec test for this method:

describe SysController, "The 'authenticate' method" do

before(:each) do
@user = User.new
end

it "should return a User object for valid users" do
User.stubs(:try_to_login).returns(@user)

result = invoke :authenticate, "valid", "valid"
result.should_not be_nil
result.should be_an_instance_of(User)
end

it "should return nil for invalid users" do
User.stubs(:try_to_login).returns(nil)

result = invoke :authenticate, "invalid", "invalid"
result.should be_nil
end

end


The line "User.stubs(:try_to_login).returns(@user)" is where Mocha works it magic. This line basically intercepts any call for the method "try_to_login" to the User object and modifies its return, all without being affected by any model logic.

Another thing to note is that the actual arguments sent to the authenticate method ("valid", "valid" or "invalid","invalid") in both tests shouldn't have any bearing on the controller logic it's testing since these arguments should only affect the model logic for try_to_login. Instead, the controller is only concerned with the return of the try_to_login method, i.e. whether @user is returned as nil or a User object.

Although this probably wasn't the best example, it can still give you a general idea on how to implement rspec testing with Actionwebservice controllers and using Mocha to mock model behaviour.