Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Sunday, 30 November 2008

Writing User Stories with Merb, Cucumber, merb_cucumber and webrat

I decided to try out cucumber and webrat for writing user stories in my sample merb application. Merbbit is a merb-based web app for community-driven link submission. Think Digg, Reddit etc.

I had used user stories before but I found so time-consuming as to be of little use. I was hoping that using webrat's shortcuts, the new improved story runner Cucumber and factory_girl would speed up writing these tests.

Getting setup was quite pain-free.

sudo gem install cucumber webrat david-merb_cucumber --no-ri --no-rdoc
merb-gen cucumber --session-type=webrat


This will also install a sample login feature that should pass if you’re using merb-auth. I was using merb-auth but when I ran:

rake features
The feature failed with the error:

Could not find field labeled "login"

This was because I had changed the login field in the login form from 'login' to 'email'. This was easy to fix. In features/login.feature I just changed

And I fill in "login" with "i_dont_exist"
to
And I filled in "email" with "i_dont_exist"
And it worked!

One of the first cool things I notice about cucumber is how much more colourful it is than the original user stories. It also discreetly tells you what line and what file the step starts on. This can be useful when you forget which file you wrote the matching step in.
And I fill in "password" with "and_i_dont_have_a_password"  # features/steps/common_webrat.rb:16
The failed login scenario of the Login feature comes for free. I now needed to write a successful login scenario.
Scenario: Successful Login
Given I am not authenticated
And an user exists with login "me@example.com" and password "secret_password"
When I go to /login
And I fill in "email" with "me@example.com"
And I fill in "password" with "secret_password"
And I press "Log In"
Then the login request should succeed
And I should see "me" in user_login
I had to write some new steps before I could run this scenario.

For the step "And a user with login "me", email "me@example.com" and password "secret_password"" I needed to create a step that would populate the database with a user. I decided to use factory girl
$ sudo gem install thoughtbot-factory_girl

add require 'factories' to features/env.rb

features/factories.rb:
gem 'thoughtbot-factory_girl'
require 'factory_girl'

Factory.define :user do |u|
u.login 'me'
u.email 'me@example.com'
u.password 'password'
u.password_confirmation 'password'
u.active 'active'
end

require 'features/steps/common_factory_steps'

features/steps/common_factory_steps:
Given /^a user with login "(.+)", email "(.+)" and password "(.+)"$/ do |login, email, password|
Factory(:user, :login => login, :email => email, :password => password, :password_confirmation => password)
end
For the step "Then the login request should succeed", merb_cucumber comes with a "Then the login request should fail" but not a succeed step. It's easy to create one though.
Then /^the (.*) ?request should succeed/ do |_|
response.should be_successful
end
I can't see an easy way using merb to tell if a specific template has been rendered like in rails integration testing (render_template) so I settled for just verifying that the users login "me" appears inside an element called 'user_login'.
Then /^I should see "(.+)" in (.+)$/ do |message, id|
response.should have_xpath("//*[@id=\"#{id}\" and text()=\"#{message}\"]")
end
This step could be used to test for any piece of text inside an element with a given id.

Sources that helped:
http://github.com/david/merb_cucumber/tree/master
http://github.com/brynary/webrat/tree/master
http://rubybling.blogspot.com/2008/10/merbcucumber-almost-baked.html

Thursday, 10 January 2008

Merb Gotchas

I'm gonna to keep this post up to date with any gotchas or "mistakes of understanding" that I make as I work on MERB.

First up. Controllers are called the same as models! Example:

$ script/generate controller monkey
Started merb_init.rb ...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
exists app/controllers
create app/controllers/monkey.rb

and
$ script/generate model monkey
Started merb_init.rb ...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
exists app/models
create app/models/monkey.rb


So if you create a controller for monkeys and a model for monkeys, the two class names will both be called Monkey, except one will inherit from controller and the other from model.

In fairness to MERB the generators do warn you about this.

"The name 'Monkey' is reserved."


But it doesn't explain why it is reserved and maybe I'm just thick but it stumped me for longer than it should have.

You never have a chance to make that mistake in Rails because controllers are always like "MonkeyController". (I don't know why merb didn't follow suit... DRYer? simpler?)

What I did to get around that was to make the controller plural. monkeys.rb

Wednesday, 9 January 2008

Enabling Sessions in MERB

Sessions are disabled by default in MERB (because it makes the performance numbers look great! ;) ). To enable sessions, just set the variable :session_store: in merb.yml to one of activerecord, sequel, datamapper, memory, cookie or mem_cache.

Because I want to use memory for my development sessions and mem_cache for my production environment, I set up config/environments/development.yml and put in:

:session_store: memory
:memory_session_ttl: 3600


and config/environments/production.yml and put in:

:session_store: mem_cache


If you're using memcached with MERB, you need to put


require 'memcache'
CACHE = MemCache.new('127.0.0.1:11211', { :namespace => 'my_app' })


in your merb_init.rb (http://merb.rubyforge.org/classes/Merb/MemCacheSession.html)

Tuesday, 8 January 2008

HTTP Authentication for MERB

I've just started working on a new web application. It's going to be quite a simple app but speed and responsiveness is important so I decided to try Merb. I'm going to blog about some of the difficulties and (hopefully) solutions I discover as I go through the process. One of the main problems with MERB is the lack of documentation so hopefully with these blog posts, I can help with that.

First up, HTTP authentication. In the merb.yml it says the following:

# If you want to protect some or all of your app with HTTP basic auth then
# uncomment the following and fill in your credentials you want it to use.
# You will then need to set a 'before' filter in a controller. For example:
# before :basic_authentication
#:basic_auth:
# :username: ezra
# :password: password
# :domain: localhost

All well and good, but what if you want to validate the authentication against a database of users?

Well the answer is to overwrite the authenticated? method in BasicAuthenticationMixin

This is easy to do, in your application.rb, do something like this:

class Application < Merb::Controller
before :basic_authentication

private
def authenticated?
# if you want get the username and password that was inputted via http use:
# username, password = *credentials
# then validate the user by authenticating against a datbase or whatever
# make sure the return value is true or false
end
end


Note: You still have to uncomment the basic_auth configuration in merb.yml. But it doesn't matter what the username/password is there since you'll be overriding them anyways.

Saturday, 3 February 2007

Using Rails with JRuby

In this post I'm going to discuss how to get a rails application running using JRuby. It's not actually not that difficult to just get it running. (The real difficulties arrive when you get to databases and other areas of rails that arent fully covered by JRuby) I'm only going to be launching the application using the WEBrick server at script/server. If you were putting this into production mode you would want to use one of the many many deployment solutions like using glassfish, tomcat and stuff like that. I'm not going to get into that stuff in this post. WEBrick is good enough for development work and as a first step.

If you don't already have JRuby installed, have a look at my previous post.

If you've just installed JRuby and stroll into a rails application and type
# jruby script/server
You will get this error
Cannot find gem for Rails ~>1.2.1.0:
Install the missing gem with 'gem install -v=1.2.1 rails', or
change environment.rb to define RAILS_GEM_VERSION with your desired version.
This is what I did and I was stumped by it at first because I already had version 1.2.1 or rails installed on my system. What was wrong with JRuby? Why didn't it realise this?

I then discovered that actually JRuby has its own gem system. You need to install rails as a JRuby gem.

Check out http://www.headius.com/jrubywiki/index.php/JRuby_on_Rails for more detail but basically you use

# $JRUBY_HOME/bin/gem install rails -y --no-ri --no-rdoc

to install rails as a gem for JRuby. This should produce this (assuming you have JRUBY_HOME defined):
Bulk updating Gem source index for: http://gems.rubyforge.org
Successfully installed rails-1.2.1
Successfully installed rake-0.7.1
Successfully installed activesupport-1.4.0
Successfully installed activerecord-1.15.1
Successfully installed actionpack-1.13.1
Successfully installed actionmailer-1.3.1
Successfully installed actionwebservice-1.2.1
Great!

Now go into a rails app:
$ cd ~/webprojects/mygreatnewrailsapp.com
$ jruby script/server
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2007-02-03 13:48:03] INFO WEBrick 1.3.1
[2007-02-03 13:48:03] INFO ruby 1.8.5 (0) [java]
[2007-02-03 13:48:03] INFO WEBrick::HTTPServer#start: pid=29324282 port=3000

Woohoo!
Go http://localhost:3000/ in your browser and if its a brand new application you should see the "Welcome to Rails page"

WEBrick is OK, but what about Mongrel?
While Mongrel is mostly Ruby, but it also contains some C (for speed reasons). JRuby can't do anything with C so for now Mongrel is out (More about that here.). What is necessary is the conversion of the C components in Mongrel to Java but as far as I know no-one has done that yet.

Installing JRuby on Ubuntu

This post is going to detail the steps I had to take in installing JRuby on my Ubuntu system. Note: these steps would probably work just fine on most other Linux systems but I haven't tried it so I cant say for sure.

You will probably need to install all this as root.

Prerequisites

The first thing you need is Java. You need the Java JRE and JDK (at least version 1.4 according to this http://svn.codehaus.org/jruby/trunk/jruby/README). I'm not going to go through the details so Ill just point you towards some tutorials
https://jdk-distros.dev.java.net/ubuntu.html - the easy way
http://java.sun.com/javase/downloads/index.jsp - the less easy way

To verify you have Java, just check the version. You should get something like this:
# java -version
java version "1.5.0_08"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_08-b03)
Java HotSpot(TM) Client VM (build 1.5.0_08-b03, mixed mode, sharing)
Next thing to install is Ant, its a prerequisite for building JRuby.
# apt-get install ant
Verify you have ant:
# ant -version
Apache Ant version 1.6.5 compiled on July 5 2006
Building JRuby

If you have Subversion you can just go
# svn co http://svn.codehaus.org/jruby/trunk/jruby
If you dont have Subversion you can download it here: http://dist.codehaus.org/jruby/ and extract it using:
# tar -xzvf jruby-src-LATEST.VERSION.tar.gz
(At this point it might be a good idea to move the jruby folder to somewhere like /usr/local/lib just to make it more globally available and less specific to any particular user. This location, where ever you decide, will be called JRUBY_HOME)

Then:
# cd ./jruby
# ant clean jar
You should get output like this:
Buildfile: build.xml

init:

clean:
[delete] Deleting directory /usr/local/lib/jruby/build
[delete] Deleting 2 files from /usr/local/lib/jruby/lib
[delete] Deleting directory /usr/local/lib/jruby/docs/api

init:

prepare:
[mkdir] Created dir: /usr/local/lib/jruby/build
[mkdir] Created dir: /usr/local/lib/jruby/build/classes
[mkdir] Created dir: /usr/local/lib/jruby/build/classes/jruby

blahblahblabh java ant creation stuff filler text blahblahblah

jar-jruby:
[jar] Building jar: /usr/local/lib/jruby/lib/jruby.jar

jar-openssl:
[jar] Building jar: /usr/local/lib/jruby/lib/jruby-openssl.jar

jar:

BUILD SUCCESSFUL
Total time: 11 seconds
If you didn't get a Build Successful, you've got problems, loser! Woah, thats probably a bit harsh. But seriously, you're on your own.

Ok, great you've built JRuby but you can't run it from the commandline yet!

Add JRuby to the path
# jruby
bash: jruby: command not found

Duh-oh! You need to add jruby to your path. I'm going to assume you use Bash, but it should be similar enough for whatever shell you use.

# vi ~your_username/.bashrc

Add this:

JRUBY_HOME=/usr/local/lib/jruby # or where ever you put it.
PATH=$PATH:$JRUBY_HOME/bin
to the end of the file.

Now reload your bash profile
# source ~yourusername/.bashrc

(Note to add JRUBY_HOME to the global path, add
JRUBY_HOME=/usr/local/lib/jruby   # or where ever you put it.
PATH=$PATH:$JRUBY_HOME/bin
to /root/.profile and (/root/.bash_profile or /root/.bashrc)
http://www.troubleshooters.com/linux/prepostpath.htm )

Now try this:
# jruby -v
ruby 1.8.5 (0) [java]
As Millhouse says: Radical!

Well thats about it. JRuby is now installed and you can use it as a ruby interpreter in the same way that you would use the official ruby interpreter.
# jirb
irb(main):002:0> puts "moo!"
moo!
=> nil
irb(main):003:0>