Showing posts with label Ruby on Rails. Show all posts
Showing posts with label Ruby on Rails. Show all posts

Saturday, March 15, 2008

Behavior Driven Development in Rails - Easy and Fun!

What?

Behavior driven coding for Ruby, and especially, Ruby on Rails.

This is a summary and run-through of the excellent RailsEnvy Love Tests presentation.

Why?

  • Tests increase your confidence, and let you sleep at night.
  • Writing tests before writing you code makes you write better code.
  • Writing the tests and specifications first, you will know when you're done - all the tests pass!
  • Tests make for excellent documentation, in the sense that Examples are more powerful than Descriptions

Also - it's fun! Do it Red -> Green -> Refactor style with RSpec and Auto testing Zen.

How?

Let's consider the example of a user who has forgotten her email.

Writing good behavior specifications is like telling a short story - a good story defines your app behavior:

A user who has forgotten a password
- Should be able to enter and submit their email address
- Should be sent an email if their email address is valid
- Should be shown a notice if the email address is invalid
- Should be redirected to the login screen if their email is valid
- Should be shown an error message if the email address is not in the system, and be shown the form again.

We use RSpec to turn our story into action.

Install RSpec

Without rails (just the gem)

gem install rspec

For rails, with its beautiful helpers

ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails

./script/generate rspec

To run your tests, you just do

rake rspec

OR you turn testing into an automated game. ZenTest will run tests *every time your files change* and pop up notifications for passes/fails using Growl/Snarl.

gem install ZenTest redgreen
autotest

Coding the behavior

Now actually write the RSpec code - or just flow by turning your English into RSpec with the awesome textmate YAML to RSpec plugin. Direction for easy install.

On to the testing. It is important to keep the component we're testing separated from the rest of the system. E.g. if we want to test the controller of a user retrieving a lost password, we don't want it to be dependent on the Model part working correctly at test times.

Enter the stage: Stubs and Mocks.

You use these to manage "canned" answers. E.g. if you want to test for a user with the name Greg who is in the system and entered his email correctly, you do


@user = mock_model(User).stub!(:first_name).returns("Greg") # Let there be a Greg
@user = mock_model(User, :first_name => "Greg") # Briefer syntax
User.stub!(:find_by_email).returns(@user) # Always find Greg

What to test?

Every line of code you write. Seriously. In Rails you write so little code that this should be possible. However, make sure that you don't slip into testing Rails - it is already rather well tested!

Examples

Validation


validates_presence_of :first_name
# To test it =>
@user.should_have_at_least(1).errors_on(:first_name)


Object relation mapping


has_many :user_roles
# Test
@user.user_roles.create(:role => "Admin")
@user.user_roles.count.should == 1


UI Redirection


redirect_to :action => 'index'
# Test
response.should redirect_to(:action => "index")


Do it

Read the RSpec website - it reads like a good book
Come up with and start a New project
Keep 100% Test coverage, or at least attempt it!
Feel good about yourself
Make sure tests pass

Monday, March 3, 2008

Has_and_belongs_to_many Sqlite3 headache: "SQL logic error or missing database"

What?

How to get many to many relations working quickly with Rails 2.0.2 and sqlite3 3.4.0.

Why?

Many to Many database relations between two models (tables) requires a middle table which records the relation. This can be difficult to get right, but Rails has some awesome helper functions to get you up and running quickly.

... when it works.

When it doesn't, you're likely to see the following:

"SQL logic error or missing database"
Which is about as descriptive as "it's not working." The kink is that Active Record (rails' database interface) will try to set the id field of the relational table, which is reserved by default as the primary key and therefore cannot be set to the same value twice (which rails will do).

How?

First create the models

./script/generate scaffold Actor first_name:string last_name:string
./script/generate scaffold Movie name:string

Then create a migration to set up the relational table:

./script/generate migration AddActorsMoviesRelation

Edit ./db/migration/xyz_AddActorsMoviesRelation.rb:
(Thanks EyeDeal for the :id => false tip!)


class AddActorsMoviesRelation < ActiveRecord::Migration
def self.up
create_table :actors_movies, :id => false do |t|
t.integer :actor_id, :foreign_key => true
t.integer :movie_id, :foreign_key => true
end
end

def self.down
drop_table :actors_movies
end
end



Then build the database

rake db:migrate

And finally edit ./app/models/actor.rb and ./app/models/movie.rb:


class Actor < ActiveRecord::Base
has_and_belongs_to_many :movies
end

class Movie < ActiveRecord::Base
has_and_belongs_to_many :movies
end



You should now be able to add movies to actors and vice versa with a simple


crime_fiction = Movie.create :name => "Crime Fiction"
san_fransisco = Movie.create :name => "The Streets of San Francisco"
jon = Actor.create :first_name => 'Jonathan', :last_name => 'Elliot'
jesse = Actor.create :first_name => 'Jesse', :last_name => 'Friedman'
dan = Actor.create :first_name => 'Dan', :last_name => 'Bakkedahl'

crime_fiction.actors << jon
jesse.movies << crime_fiction
dan.movies << crime_fiction

crime_fiction.actors.each {|actor| puts "#{actor.last_name}, #{actor.first_name}" }
# => Elliot, Jonathan
# => Friedman, Jesse
# => Bakkedahl, Dan

san_fransisco.actors << jesse
jesse.movies.each {|movie| puts movie.name }
# => Crime Fiction
# => The Streets of San Francisco

Friday, February 29, 2008

ExtJS & Rails - ExtJS scaffold generator installation script

What?
An automatic installation script for the ExtJS scaffold generator plugin (demo keynotes)to get you up and running.

Why?
Rails' scaffolding gets you up an running lightning fast. ExtJS is a wonderful javascript desktop library. Let's marry them

How?
Run the following script - it will

  • Create the rails application
  • Install the ExtJS scaffolding plugin
  • Download and install ExtJS for your application
  • Generate two sample models - posts and purchases
  • Setup the database
  • Create links to the sample models on the application landing page
  • Run the server
Then all you have to do is point your browser to http://localhost:3000, assuming the server is running on your computer.

Have fun!

# Create the app
rails ext_scaffold_setup
cd ext_scaffold_setup

# Install the plugin and extjs
script/plugin install http://rug-b.rubyforge.org/svn/ext_scaffold
curl -O http://extjs.com/deploy/ext-2.0.2.zip
unzip -q ext-2.0.2.zip
rm ./ext-2.0.2.zip
mv ./ext-2.0.2 ./public/ext

# Generate some scaffolds - posts and purchases
./script/generate ext_scaffold post title:string body:text
./script/generate ext_scaffold purchase order_id:integer amount:decimal description:text

# Create the database for the models
rake db:migrate

# Create links to the models on the landing page
echo '<title>ExtJS Scaffold generator sample</title>echo '<style type="text/css" media="screen">a {text-decoration:none; color:#abc}</style><a href="http://www.blogger.com/posts">Posts</a> <a href="http://www.blogger.com/purchases">Purchases</a>' > ./public/index.html

# All set, run the server!
./script/server

Point your browser to http://localhost:3000, and you're good to go.

Monday, February 25, 2008

BackPackIT BackOffIT! ClockingIT isIT!

What?

ClockinIT: A free, beautiful, open source project management website.



"ClockingIT was created for fun, not profit, using Ruby on Rails"



While 37signal's Backpackit has had a bit of monopoly on the ajaxian project management frontier, ClockingIT recently sprung up from two dedicated Norwegian developers, and the community seems to be building strong.

ClockingIT specs: built leveraging a ton of open source goodies, including Ruby on Rails, MySQL, Juggernaut (cometing), and more.

Why?


Three main points for ClockingIT over Backpackit:

  • It's Free
  • It's Open source
  • It's Up and running in 2 minutes
How?

Go to ClockingIT.com and signup.

For developers who want to contribute, get the source with darcs (howto) or with git (repository: git://repo.clockingit.com/cit).

[git installation instructions on OS X 10.5]

Thursday, February 14, 2008

Ruby acts_as_taggable plugin bug fix for Rails 2.0.2

What?
I'm working on a small rails app and wanted tags, so I went ahead and installed the acts_as_taggable plugin. However, I had some problem with installation. First, I just ran

./script/plugin install acts_as_taggable

and followed setup procedures. However, I then got the following error when I called acts_as_taggable in my class

NameError: uninitialized constant Tag

I then found a post that directed me to the correct source for the tag:

./script/plugin install http://svn.rubyonrails.org/rails/plugins/legacy/acts_as_taggable/

However, running rails 2.0.2, the has_many API has changed, so I got the following error:

ArgumentError (The :dependent option expects either :destroy, :delete_all, or :nullify (true))

I traced the error down in the plugin to the file rails_root/vendor/plugins/acts_as_taggable/lib/acts_as_taggable.rb

The error is on line 18

has_many :taggings, :as => :taggable, :dependent => true

should be

has_many :taggings, :as => :taggable, :dependent => :destroy

Now it works!

Wednesday, May 23, 2007

Riding Rails: Ruby on Rails will ship with OS X 10.5 Leopard

Ruby on Rails logo Riding Rails: Ruby on Rails will ship with OS X 10.5 Leopard :

"It s finally official: Ruby on Rails will ship with the next version of OS X"
This is great news - while RoR allows for incredibly fast development (I just put up a user blog webapp in 35 minutes!) the setup process is a bit of a hassle for the uninitiated (if you're up for the challenge, hit up hivelogic's tutorial).