Wednesday, July 29, 2009

Building a merb application, part two

In part 1 we got our requirements and started building our first cucumber feature. We didn’t finish it so lets do that now.

First job is the enhance the feature. We will have two scenarios now, one with a successful login, and one that accepts an invite. However we are going to get a lot repetition if we have to have all those log in steps for each feature. Luckily we can call steps from within steps. Modify the setup_steps.rb as follows. We have also filled in the I have an invite step.

Given /^I have an invite$/ do
@user.invites.build if @user.invites.empty?
end

Given /^I am logged in as "(.*)" with password "(.*)"$/ do |login,password|
Given "I am not authenticated"
And "I am a user \"#{login}\" with password \"#{password}\""
And "I have an invite"
And "I go to /invites"
And "I fill in \"login\" with \"#{login}\""
And "I fill in \"password\" with \"#{password}\""
And "I press \"Log In\""

end




Now our feature looks like this





  Scenario: Login 
Given I am logged in as "myfriend" with password "apassword"
Then I should see "Are you coming?"

Scenario: Accept Invite
Given I am logged in as "myfriend" with password "apassword"
When I press "I'm coming!"
Then I should see "Accepted invite!"




To pass the new feature we need to update the application.

First we need to add an accepted property to the Invite model. We’ll actually make this a datetime so we know when they accepted.


Add this line to models/invite.rb, just under the belongs_to statement.





  property :accepted, DateTime




And in controllers/invites.rb, lets limit the invites to the current user.





  def index
@invites = session.user.invites
display @invites
end



In views/invites/index.html.erb, add this



<% invite = @invites.first %>
<%= form_for invite, :action => url(:invite, invite) do %>
<%= hidden_field :accepted, :value=>DateTime.now %>
<%= submit "I'm coming!" %>
<% end =%>




Note that we are only using the first invite, because that’s all the feature requires to pass. For BDD, we write the absolute minimum to pass the tests. Now run our features again. We should get all green!

> rake features


Part three will implement multiple invites.


To get the code clone the repository (if you haven’t already done so) and check out this commit.


> git clone git://github.com/donibuchanan/merb-rsvp.git


> cd merb-rsvp


> git checkout 67d5ebde343024baac5f0c5a704c5b3719f5310d

Saturday, July 25, 2009

Building a Merb application, part one

So we have the following requirements for a party invite app.

"I want to be able to send invites to people via email or post, then I want them to hit the page and enter a code in the invite. After this they will be able to tick their name (or names in the case of a couple) as a way to RSVP.

The dress up theme of this party is 'inappropriate' so I also want people to suggest something inappropriate to wear.

When they submit the form they will be taken to a page with detailed party info and they will get a selection of suggestions by other guests."

lets create our project and submit it to source control

> merb-gen app merb-rsvp

> cd merb-rsvp

Now we need to fix the dependencies file as detailed here.

merb_gems_version = "1.1"

dm_gems_version   = "0.9.11"

do_gems_version   = "0.9.12"

lets run

> rake db:automigrate

And submit it to source control, in this case it is http://github.com/donibuchanan/merb-rsvp/tree/master

Ok so what's the very first thing we need? Before we get into admin, we'll work on the 'welcome' page. This will be a protected resource.

So lets write the simplest possible user story.

As an invitee

I want login and accept my invite

How does this translate to code? We use cucumber, lets install that

> sudo aptitude install libxslt-dev libxml2 libxml2-dev

> sudo gem install cucumber

> sudo gem install roman-merb_cucumber

> sudo gem install webrat

> merb-gen cucumber --session_type webrat

> merb-gen feature accept_invite

Lets create a feature (which is a user story with different scenarios) to look like this

Feature: accept invite

To accept an invite

An invitee

Logs in and checks the I accept box

Scenario: Login

Given I am not authenticated

And I am a user "myfriend" with password "apassword"

And I have an invite

When I go to /invites

And I fill in "login" with "myfriend"

And I fill in "password" with "apassword"

And I press "Log In"

Then I should see "Are you coming?"




Before running features, we need to tweak the cucumber.rake file, located in lib/tasks. We change line 5 to





t.binary = '/usr/local/bin/cucumber'




Now run the feature



> rake features



Its all yellow! Don't worry about the blue bits from the authentication feature, its actually running if you see the three green lines under Examples.



Our feature is not very useful, we need to implement steps that match the text in the feature.



We will create a file under features/steps called setup_steps.rb and put this in





Given /^I am a user "(.*)" with password "(.*)"$/ do |login,password|

@user = User.new(:login=>login)

@user.password = @user.password_confirmation = password

@user.save

end

Given /^I have an invite$/ do

#we haven't defined an invite object yet

end




Now



> rake features



Gives us a couple of green lines and a bunch of red! This is good, now we build the app until it runs green.



-----------



First up, lets create invites



> merb-gen resource invite



Lets make a link between invites and users.



In models/invite.rb add this before the "end"





belongs_to :user




and in user add





has n, :invites




Lets protect the invites resource and alter the index so it only shows the invites for the current user



In config/router.rb wrap the resources call in an authenticate block





authenticate do
resources :invites
end




Lets update our invite step in setup_steps.rb for having an invite





Given /^I have an invite$/ do
@user.invites.build if @user.invites.empty?
end</p>




In merb/merb-auth/setup.rb, mixin some redirect back functionality, just before the class Merb::Authentication part.



# Mixin the redirect back mixin





require 'merb-auth-more/mixins/redirect_back'




Finally lets add change the view so the feature will pass. Replace the contents on views/invites/index.html.erb with





Are you coming?




Now run features again



> rake features



4 scenarios (4 passed)



YES! I know it was the simplest of features but this was to give you an idea of what a feature was, if you haven't seen them before. 



Part two will complete the user story, currently the user is not accepting the invite.


To get the code clone the repository and check out this commit.


> git clone git://github.com/donibuchanan/merb-rsvp.git


> cd merb-rsvp


> git checkout 8e6695ac746a67e01edb08f7fea4e48878b8d3d9

Wednesday, July 22, 2009

Pimp My Gedit, quickly

We all know gedit is very basic and can be improved greatly, as detailed in Pimp my Gedit. However, there's other, and easier ways, of doing this. In addition, there's a lot of fiddling around with the instructions there, and I don't really use a lot of stuff he recommends chasing down. Gmate is a godsend, collecting up a bunch of stuff for gedit, very helpful. However, I like a different auto complete plugin so I've forked it here.

To download and install gmate, in a terminal, type:

> git clone git://github.com/donibuchanan/gmate.git

> cd gmate

> sh install.sh

Answer yes to everything. Now to set it up.

Fire up gedit and go to Edit->Preferences

In the View tab make sure you have the following options checked

view

In the Editor tab, make it look like this

Editor

Fonts and Colours do this: Of course you can choose different ones but I've found these to be the best

fonts

Ok for the list of plugins, these are the ones I enable, you can choose different ones.

Advanced Bookmarks
Autocomplete (this relies on open files, so keep those tabs open)
File Browser Pane
Find In Files
Gemini
Go to File (ctrl-alt-o to use)
Rails File Loader
Regex Search and Replace
Snippets (although i rarely use it)

Feel free to play around with all the plugins and if you think I should be using one, please tell me!

So now you have gedit, looking good and helping you out. Just make sure the root of you project is the root in the file browser pane.

Tuesday, July 21, 2009

The Complete Guide to getting Merb running on Ruby 1.9 with Ubuntu 9.04

We are going to get merb up and running with ruby 1.9.1 on ubuntu 9.04(Jaunty Jackalope). I'm not going to assume a lot of knowledge of ruby or merb and only basic knowledge of linux and ubuntu. I assume you know what a terminal is and how to get around the file system. This guide is in two parts, installing ruby and installing merb.


Part one, installing Ruby 1.9.1

I've got a freshly installed copy of Ubuntu 9.04 here, so the very first thing to do is install the tools required to compile stuff. Fire up a terminal and type

> sudo aptitude install build-essential

Now to install ruby, unfortunately the ubuntu version of ruby is 1.9.0, which is a bit out of date, so we'll build ruby from sources.
Download ruby from

http://www.ruby-lang.org/en/downloads/, latest at time of writing is Ruby 1.9.1-p129.

Extract the file, the exact file may differ

> tar -xvzf ruby-1.9.1-p129.tar.gz
> cd ruby-1.9.1-p129

Now we're in the source of ruby, lets build it.
Before installing we need a couple of prerequisites for building ruby.

> sudo aptitude install libz1g-dev libssl-dev libreadline-dev

Now type

> ./configure

And you'll see a bunch of stuff, let it run.

When its finished type

> make

and let that run. Ruby is built and ready to install.

Before we install ruby, lets install checkinstall, this will allow us to cleanly remove our installation cleanly, in case we stuff things up, or we want to switch to the official ubuntu version later. Go head and install it.

> sudo aptitude install checkinstall

Now run

> sudo checkinstall –pkgname libruby1.9
> Should I create a default set of package docs? Y
> End your description with an empty line or EOF
Ruby 1.9.1
> Enter a number to change any of them or press ENTER to continue: [press enter]
> Do you want me to list them? [n]: n
> Should I exclude them from the package? (Saying yes is a good idea) [y]: y

It might take a while at "Copying files to the temporary directory" but it will finish eventually.

Alright, ruby installed! Lets check it

> ruby -v
ruby 1.9.1p129 (2009-05-12 revision 23412) [x86_64-linux]

YAY! Your output might be slightly different, especially if you’re on a 32bit system but don't worry about that.

Part Two, installing merb

Now to install merb.
Ruby stuff is all installed via a lovely tool called rubygems, which uses the command 'gem', not 'rubygems'. Lets see what version it is now.

> gem -v
1.3.1

That’s slightly out of date, the current version is 1.3.5, lets update rubygems itself.

> sudo gem update --system

We also need sqlite3 for our default database engine for merb so lets install that

> sudo aptitude install sqlite3 libsqlite3-dev

Now, finally, we can install merb. We will install the standard package, which includes all our dependencies, and then upgrade to edge, which has some nice features and bug fixes. Ignore errors with the documentation installation.

> sudo gem install merb
> sudo gem install merb --source http://edge.merbivore.com

MERB INSTALLED! Lets run something

> merb-gen app my_first_app

/usr/local/lib/ruby/gems/1.9.1/gems/merb-core-1.0.12/lib/merb-core/dispatch/router/behavior.rb:18: warning: undefining `object_id' may cause serious problem
/usr/local/lib/ruby/gems/1.9.1/gems/merb-core-1.0.12/lib/merb-core/dispatch/router/behavior.rb:18: warning: undefining `__send__' may cause serious problem
/usr/local/lib/ruby/gems/1.9.1/gems/templater-0.5.0/lib/templater/core_ext/string.rb:5:in `realign_indentation': undefined method `to_a' for #<String:0x00000002cc7bb0> (NoMethodError)
. . .

That didn’t work out too well!


Turns out we need to get the latest version of templater so first up we need git, a source control tool.

> sudo gem install git

Now run

> git clone git://github.com/jnicklas/templater.git
>
> gem build templater.gemspec
> sudo gem install templater-0.5.0.gem

Lets also install this gem, i really have no idea what it does but its required :)

> sudo gem install methopara

Let’s try again

> merb-gen app my_first_app

SUCCESS!

Let's try to run it.

> cd my_first_app
> merb
Loading init file from /home/donibuchanan/Documents/my_first_app/config/init.rb
Loading /home/donibuchanan/Documents/my_first_app/config/environments/development.rb
~
~ FATAL: The gem data_objects (= 0.9.11, runtime), [] was not found
~

Hrmm, thats not so good is it? Your error might be a bit different in terms of version numbers and the exact message. Turns out merb dependencies are really hard coded currently, so you have to go change them manually.

First we will get version numbers we need so run

> gem list

Note the version numbers for these gems, I've put the version I have currently
merb (1.1)
dm-core (0.9.11)
data_objects (0.9.12)

We will run gedit to edit it. I'll write another article in beefing up gedit capabilities later on.

> gedit config/dependencies.rb

You'll need to edit these three lines to match the version numbers you noted before
merb_gems_version = "1.1"
dm_gems_version = "0.9.11"
do_gems_version = "0.9.12"

Before we try again lets install thin and ( a web server)

>

And finally lets run it

> merb -a thin

Browse to http://localhost:4000 and see the glory!

IT LIVES!

What now? How about doing some examples from the samples page? Or start following my sample app?