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.

Thursday, December 28, 2006

has_many :through associations and the push (<<) operator.

Background:
I needed to create a many-to-many association between two of my models, called Band and Show, i.e. a band can be a part of many shows, and many shows have bands performing. I considered the traditional method of using a has_and_belongs_to_many (habtm) association and creating a "bands_shows" join table. However, the associations themselves needed to contain their own set of information. This is, of course, still possible with a habtm association. Unfortunately, it's more of a hack than an elegant solution.

This is where has_many :through associations come in. has_many :through is a method for creating a many-to-many association between two models and contain a set of information within themselves. There is sufficient documentation of it through Google already, so I won't go over all the details. For a comparison between habtm and has_many through association, visit http://weblog.rubyonrails.org/2006/04/21/habtm-vs-has_many-through/

Problem:
For my project, I mentioned above that the two models I was trying to associate were Show and Band. I decided to call their associating join table "Performance," since a band's relationship to a show is called a performance. I set up my database and Rails models, and everything was going great... until I tried to use the push operator, e.g.
Show.find(:first).bands << Band.find(:first)
This is where my world came crashing down. Since, at this point, I've only used habtm associations, I was expecting to be able to add these relationships just as easily as using a push operator. Oh, how wrong was I. A Google search then revealed to me that the push operator does not work with has_many :through associations. SADFACE, I thought. Will I have to resort to creating a method in the Show model called add_band and a method in the Band model called add_show? No, that's ugly. I refuse.

Solution :)
A little more research then revealed that a push operator solution was already in the works and was implemented in Rails edge. Unfortunately, I couldn't (or didn't want to) upgrade to edge, so I decided to implement my own push method and remove it when Rails stable implemented it, thereby not having to edit any of the method calls.

Band.rb:

class Band < ActiveRecord::Base
has_many :shows, :through => :performances, do
def <<(show)
begin
Performance.create!(:show => show, :band => @owner)
self.concat([show])
rescue
nil
end
end
end
end

Pretty simple, isn't it? What's important to note is @owner. Rails is a little wonky with the way it handles closures, so when you're in the has_many :shows block, "self" actually references the association array, rather than the Band object. In any case, there it is. Here are the other two models, just in case you need them.

Show.rb:

class Show < ActiveRecord::Base
has_many :bands, :through => :performances do
def <<(band)
begin
Performance.create!(:show => @owner, :band => band)
self.concat([band])
rescue
nil
end
end
end
end


Performance.rb:

class Performance < ActiveRecord::Base
belongs_to :band
belongs_to :show
end

Sunday, November 19, 2006

My Dev Blog

My name is Greg. I'm a software engineering student at McGill university, and an aspiring software architect/developer.

To kick things off, I will first outline what this blog will NOT be about. This blog will not be about my personal life, or current events (unless it's related to software).

This blog will be all about my adventures in software development, programming, problem solving and school (which is an on-going problem I will be trying to solve). I hope that this blog will help others, but I'm mainly using it as a record of my work.