Friday, October 24, 2008

REST: Some tips and implementing "Forgot your password?"

If someone asked me what was the best thing I had learnt over the last 12 months, I would say: REST.

I know that the RESTful approach requires changing our way of thinking, and it took me a while to "switch". The benefits are huge. From my experience the main advantages of REST are:
  • the consistency it brings to the app,
  • a standardized way of creating controllers,
  • thinking always in terms of resources - what is the 'thing' that the user wants to create/update/destroy?
  • a higher-lever abstraction that simplifies communication with other people in the team
  • very often there's only one RESTful way of implementing a certain feature
  • consistent API, in one project we use Flex for the UI, and being RESTful makes it very easy to work with Rails controllers.
"Forget your password" as an example

One of the examples that is hard/not worth (according to some people) to implement in a RESTful way is "Forgot your password" functionality. Some people feel like it could be just "/users/4/reset_password" and that we could use a custom action for the users controller. I know that it can work, but I see no reason to break the REST rules here (by adding a non-REST action to the controller).

My RESTful solution

The feature should let users provide an email, send them a link to a page, where they can change their password.

We have several steps to follow:
  1. Show a page where people can type their email, verify the email
  2. Generate a reset_password code.
  3. Send an email with this link.
  4. Clicking on this link should check if it's a valid reset_password code
  5. If it is valid, then show a page where a user can type the new password twice.
  6. Display a message appropriate for the result (error when the passwords don't match etc.)

Given these steps, I split the code into the following controllers and actions:

Email Verification

We want to let people create a new Email Verification, so:

I created an EmailVerificationsController with new/create actions.

It is responsible for:
  • finding the user by email,
  • generating the reset_password code,
  • sending the email,
  • handling errors
Passwords

After veryfing the email, we want people to change (update) their Password, so:

PasswordController with edit/update actions

it's responsible for:
  • authenticating the user by the reset_password code,
  • letting the user change the password,
  • displaying errors.

That's it.

Techniques

This example shows that a controller doesn't have to reflect a ActiveRecord class. There is no such model as EmailVerification in my code. It could be worth considering it, but in my case I don't think it was needed.

A useful technique for REST-izing your design is to turn verbs into nouns, thus making them easier to expose as resources. In this case, we wanted to verify an email, so I turned it into "creating an EmailVerification resource".

Sometimes changing the words slightly can help. Instead of resetting passwords, I use "update a Password".

As always, I try to move as much logic to the model as I can. Ideally I just call one model method and handle exceptions if there are any. See this article for more details about this approach.

A controller can use other models. In our case the PasswordsController could call User.update_attributes method to change the password.

Bonus

A friend of mine asked recently how I'd implement a feature, that let's an admin making another user a superuser.

My thinking here is that I turn the "making someone a superuser" into "creating a new superuser". This shows me that we now have a resource called Superuser. According to REST a Create action expects the params to be sent using POST. So we can just POST the id of the user to the Create action. In the Create action we just call admin.make_superuser(params[:id]).

Questions

What is your opinion about these solutions?
Would you solve them differently?

Feel free to ask me how I'd RESTfully implement other features.

Monday, July 7, 2008

MVC: How to write controllers

I've been working on many projects (web and desktop applications), and I must say that the way MVC is implemented varies a lot. That's probably a good thing, since every project is different. I believe, however, that there are some common patterns that could improve most of the MVC applications.

1. Keep it simple


Don't create additional tiers unless they are needed. Every class should clearly belong to either Controller, Model or a View.


2. Thin controller, fat model


This is probably the most useful lesson I learnt from the Rails world. I'm not going to elaborate on that here, as it was covered in many other places. Just move all your logic to models. It's that simple.


3. RESTful design

If you work on a web application, and still don't know what REST is, then go and read about it. It's easy and it will simplify your design a lot. Basically, it makes you think about your app as a set of resources. Every action in your controllers does one of the following with your resources: new, create, edit, update, show, destroy, index(list). At first, it can feel as a limitation, but it is the kind of limitation that actually helps you.

Many people argue that not everything can be RESTified. I agree that there are places where REST is not needed, however it's always a good exercise to find a RESTful solution.


4. Communication between controllers and models

As always, when you delegate some work further, you need to control the result somehow. The same happens when you move all the logic from controllers to models.

There are three ways (that I'm aware of) of implementing the communication between a controller and a model.

4a. Return codes

You use return codes when your model methods return not the data, but the result of an operation.

An example here is ActiveRecord::Base.save method which returns true when the save was successful, false otherwise.

order = Order.new(params[:order])
if order.save
flash[:message] = "The order was created!"
redirect_to order
else
flash[:errors] = "Something was wrong"
redirect_to new_order_path
end

In my opinion it's a good solution for simple situations. However, I don't like the "if" condition here and also it can go messy with more complex examples.

4b. Ask the object for its state


credit_card = CreditCard.new(params[:credit_card])
if credit_card.valid?
if order.capture_payment(credit_card)
flash[:message] = "success"
else
flash[:message] = "payment failure"
end
else
flash[:message] = "credit card validation error"
end
redirect_to order
This is slightly better than a), because the return code knowledge is hidden in method calls, but we still see the problem of many "if" statements.

4c. Don't ask, tell. Use custom exceptions

That's my favorite, because it eliminates all the "if"s from my controller code. I know it's just a syntactical change, but I prefer to see something like this in my controller, rather than the example above:

begin
order.pay(credit_card)
flash[:message] = "success"
rescue CreditCardNotValid
flash[:message] = "credit card validation error"
rescue PaymentFailed
flash[:message] = "payment failure"
end

Here we use custom exceptions to define a "protocol" for the communication between a model and a controller. Obviously we need to define the exceptions somewhere. I like to add them at the top of the associated model class:

class CreditCardNotValid < Exception
class PaymentFailed < Exception

class Order < ActiveRecord::Base
def pay(credit_card)
...
end
end


So, what are your favorite patterns for writing controllers?

Thursday, June 26, 2008

Git: working with branches

The way we work with git is that for every remote pair programming session we create a separate branch. We give it a name after the feature we're working on.


git checkout -b feature1


It automatically switches to this branch.

During our work we tend to checkin our changes quite often. We do it with:


git checkin -a -m "created some specs for class A"


After we finish our session, we do two things.
First, we merge our branch to master:


git checkout master
git merge feature1


Then, we delete the branch we were working on:


git branch -D feature1


That's it.

If you happen to delete the branch BEFORE you merge it, don't panic, there is a solution.
In order to undelete a branch just after you deleted it, do:


git checkout -b new_branch_name HEAD@{1}

Tuesday, June 10, 2008

Andrzej's Rails tips #11

Today I'm going to show you two tips, both related to Rails controllers.

Use the current_user object whenever you access its data

Instead of

@order = Order.find(params[:order_id])

do this:

@order = current_user.orders.find(params[:order_id])

Thanks to that, you don't have to check whether this order belongs to the user, you just need to handle ActiveRecord::RecordNotFound exception.

Move all the logic from your controller to the model

I know you already read this statement many times, but I will repeat it anyway.
In your actions you shouldn't manipulate your model objects, do it in the model class itself. Here's a simple example:

BAD CODE:

class OrdersController < ApplicationController
def update
@order = current_user.orders.find(params[:order_id])
if params[:order][:amount] > 0
@order.prepare_invoice
@order.send_email
@order.mark_as_paid
@order.notify_producers
end
end
end

BETTER CODE:

class OrdersController < ApplicationController
def update
@order = current_user.orders.find(params[:order_id])
@order.pay(params[:order][:amount])
end
end

class Order < ActiveRecord::Base
def pay(amount)
if amount > 0
prepare_invoice
send_email
mark_as_paid
notify_producers
end
end
end

Friday, June 6, 2008

Andrzej's Rails tips #10

form_for and namespace route

When you use a namespace route like the following:


map.namespace :admin do |a|
a.resources :users
end


then if you want to use form_for @user, this is the correct way:

<% form_for([:admin, @user]) do |f| %>

2 minutes with David Chelimsky and RSpec stories (video)

A short (2.34 minutes) description of RSpec stories.


David Chelimsky at Railsconf 2008 from Gregg Pollack on Vimeo.

Wednesday, May 21, 2008

Story Driven Development

If you are interested in testing and the TDD/BDD movements, then you may find Bryan Helmkamp presentation on "Story Driven Development" worth watching. Bryan explains the differences between unit testing and scenarios. He also shows Webrat, a tool he works on, which makes defining stories in Rails apps very easy.

I like the title that Bryan has chosen for the talk. The name "Story Driven Development" makes it clear that it's a lot about defining requirements, and not only about testing. Additionally, I think it's clear that it focuses on the acceptance level of tests without mentioning unit tests.

He also provides a quote from Robert Martin:

Unit tests: “Make sure you write the code right.”
Scenarios: “Make sure you write the right code.”

You can watch the slides here, or grab the PDF version (which has correct code examples).

Tuesday, April 8, 2008

Google App Engine and dynamic languages

Google has launched Google App Engine. It's a big news. But look at this:

"Although Python is currently the only language supported by Google App Engine, we look forward to supporting more languages in the future."

You see, Python is their first choice as the main language for Google App Engine. It's not that surprising, Google is known to be a Python company. I'm pretty sure that Java, .Net and Ruby support will be available very soon as well. I wonder when will be the first time that some of the Google (or other companies) services are available only with dynamic languages.


What is my point here?

If you are serious about your career in IT then you have to start learning how to develop software with dynamic languages.


Python or Ruby seem to be good candidates. If you ask me - if you come from .NET world go for IronPython, if you come from Java choose JRuby. That's a good start. Good luck!

Wednesday, April 2, 2008

BDD examples with user stories and Webrat

This is the best BDD explanation I have ever seen. It explains all the philosophy behind BDD, shows how it fits with RSpec stories, and how you can use Webrat to create a high-level integration test. It even shows how to use Selenium with RSpec stories!

Integration Testing in Ruby with RSpec's Story Automation Framework by David Chelimsky

Big thanks to David Chelimsky!

Thursday, March 27, 2008

Going to Euruko 2008?

Oh, how I love the conference season!

After attending the SFI conference in Krakow (which was great, BTW!) I'm ready to go to Prague.I'm going to be at the Euruko 2008 conference. Their program looks really interesting! All things that I like: Ruby, JRuby, testing. Hey, there's even a talk about AOP in Ruby!

I'll be in Prague from Friday afternoon till Sunday evening, and I'm always happy to chat about Rails, TDD, Agile, JRuby.

I'll try to comment about the conference on my Twitter. Peter Cooper is also going to update his twitter. Any other Twitterers in Prague?

See you in Prague!

Andrzej's Rails tips #9

link_to_remote and GET request

If you're working with link_to_remote and you are surprised with a message like the following:

Only get, put, and delete requests are allowed.

then you just need to know that link_to_remote uses POST request by default. All you need is to do is to add :method => :get to the method call.

Use schema.rb to create a new database

Keep the schema.rb file in your Subversion/Git/Mercurial repository and make sure it's up-to-date. It's very useful when you want to create a database without using migrations. You just call rake db:schema:load.

As the documentation states:

"Note that this schema.rb definition is the authoritative source for your database schema. If you need to create the application database on another system, you should be using db:schema:load, not running all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations you'll amass, the slower it'll run and the greater likelihood for issues)."

Monday, February 25, 2008

Andrzej's Rails tips #8

Resource controller and 'new' action

If you want to refer to the 'new' action using resource controller, you have to use the 'new_action' method.
BTW, if you want to do the same operation before both 'new' and 'edit' actions in a resource controller, you can do it like that:

[new_action, edit].each do |action|
action.before { @product_types = ProductType.find(:all) }
end

Migrations and removing defaults

If you have some default values set on some columns, then you can remove them using ':default => nil'.
Like that:

change_column :users, :name, :string, :default => nil

Tuesday, February 19, 2008

Andrzej's Rails tips #7

Dumping and loading data

Recently, I was in a need to dump data from a sqlite database and load it to a mysql database. There are many ways of doing such a task. One of them is using a plugin released by the Heroku people - YamlDb.

rake db:data:dump
rake db:data:load

You can install it with the following:

script/plugin install http://opensource.heroku.com/svn/rails_plugins/yaml_db

attachment-fu and capistrano

I use attachment-fu for one of my projects. One of the open questions here is how to deal with the files that our users uploaded to the server. I use capistrano 2, and ideally I prefer to have everything automated. The way you can use capistrano to deal with uploaded files is to have a directory which will be shared across different releases (the same as logs).

First, we need to tell attachment-fu that the upload directory is going to be public/uploads. Then, we need to tell Capistrano that public/uploads is going to be a shared directory. Both those things are nicely explained in the following article:

Working with attachment_fu

Monday, February 18, 2008

Andrzej's Rails tips #6

Redo a migration

There is a new rake task for redoing a migration:

rake db:migrate:redo

BTW, if you need to do it on a production server, then just append RAILS_ENV:

rake db:migrate:redo RAILS_ENV=production

and of course, if you want to list all rake tasks:

rake -T

Migrating to Rails 2.0 and mass-assignment warning

I was upgrading one application from Rails 1.2.3 to Rails 2.0. The application uses restful_authentication. One of the problems we have seen, was a warning in the log, saying something like 'mass-assignment no longer supported'. After investigation we found out that the problem was updating the user object. The User model was generated with restful_authentication, but later there were some application specific attributes added by a developer. Since restful_authentication generates a line like that:

attr_accessible :email

it means other attributes are not accessible. Adding the new attributes to this list solves the problem.

atttr_accessible :email, :birthdate

Monday, February 11, 2008

Andrzej's Rails tips #5

Two things today, both related to RSpec stories: webrat, and using regexps in RSpec stories.

Webrat with RSpec stories

What is Webrat? "Webrat lets you quickly write robust and thorough acceptance tests for a Ruby web application". It uses Hpricot under the hood and is very easy to understand just by looking at the code.

It took me only 30 minutes to turn most of my tests in one of my applications from a classic IntegrationTest-based RSpec story to Webrat. Here is one example:

When "he creates an order" do
visits '/'
clicks_link "New order"
fills_in "Nr", :with => 'abc/2008'
fills_in "Company", :with => 'ABC company'
selects 'New'
clicks_button 'Create'
end

Thanks to Ben, for his great article describing RSpec stories with Webrat.


RSpec, response.should have_text


Sometimes, all you need is just a check whether there is a certain message visible on a page. One way of doing that is with regexps. Here is an example step that checks for the message:


Then "he sees a $message" do |message|
response.should have_text(Regexp.new(message))
end

Thursday, February 7, 2008

Andrzej's Rails tips #4

will_paginate in other languages

The default labels for the will_paginate are 'Next' and 'previous'. It's trivial to change them like that:

<%= will_paginate :prev_label => 'Wstecz', :next_label => 'Dalej'%>

Rails plugins, Piston and URLs

I use piston for plugins management. It's a nice tool that allows you to call 'piston up' on any of the plugins, at any time to update. What it does worse than 'script/plugin' command is managing different repositories. With the 'plugin' command you can say:
script/plugin source URL
and it adds the URL to the list of known repositories.

What I use with Piston instead, is storing the urls as environment variables. Here is an excerpt of my ~/.bash_login file:

export ATTACHMENT_FU=http://svn.techno-weenie.net/projects/plugins/attachment_fu/
export RESOURCE_CONTROLLER=http://svn.jamesgolick.com/resource_controller/tags/stable/
export ATTRIBUTE_FU=http://svn.jamesgolick.com/attribute_fu/tags/stable
export RESTFUL_AUTHENTICATION=http://svn.techno-weenie.net/projects/plugins/restful_authentication/
export RSPEC=http://rspec.rubyforge.org/svn/tags/CURRENT/rspec
export RSPEC_ON_RAILS=http://rspec.rubyforge.org/svn/tags/CURRENT/rspec_on_rails

So I just type 'piston import $ATTA', press TAB, and the shell autocompletion does it for me.

Wednesday, February 6, 2008

Andrzej's Rails tips #3

Multi-model forms with attribute_fu

I've used attribute_fu in two of my projects now. In one of them I use it in three different places. It simplifies your code a lot, when you want to edit many models in one form. Strongly recommended!

attribute_fu

Gradual switch to resource_controller

resource_controller is a plugin that allows your controller's code to ... disappear :-)
A great thing with this plugin is that you can introduce it gradually. Just install it, and make one controller a resource controller. See how it goes, and then decide whether it was worth it. I did it like that, and now I'm switching to it wherever I can.

BTW, both attribute_fu and resource_controller are made by James Golick. Thanks, James!

restful_authentication with RSpec

I didn't know it before, but Bartosz Blimke discovered that, when you have a spec directory in your Rails app and you generate some resources using restful_authentication, then it will generate Rspec specs instead of unit tests. Cool! I'm not a big fan of code generation/scaffolding, but when they also provide good specs then it easies the pain.

Tuesday, February 5, 2008

Remote pair programming

This article describes my experience with remote pair programming, why I chose remote pair programming for one of the projects, how it works and what tools we use.

One of the projects I'm working on is a Rails application. We work on that together with my colleague, Marcin. There's nothing really innovative in this application. I've got enough experience with Rails and IT in general to know that it can be finished in a given time. The interesting bit is, that both me an Marcin live in different cities. Even better, both of us travel a lot and very often we are in different countries.

A distributed team is nothing new in today's IT world. One of the most common approaches to this problem is to divide the system and responsibilities across different people/teams/locations. It can work, I was involved in many such projects. The problem is that it requires a lot of trust, patience and really good skills to know how to split the project. In my opinion, it's a very risky approach, and it's easy to do it wrong. Chad Fowler describes it perfectly in his book: "My job went to India".

Traditional pair programming is a great practice. I know it's still controversial, and I agree that it doesn't have to work for all software developers and all projects, but I've seen it successful often enough to know that there are many benefits of pair programming. The benefits that I find important are:
  • Focus on the task
  • Sharing knowledge
  • Increasing trust
  • Fun
  • Speed of development
In our case, I think all of the factors are important. As always, time is a constraint, so it's important to focus on the most important tasks first. We have different skills - while I'm a little bit more experienced with Rails and TDD, Marcin is my guru when it comes to html/css. He worked on many different IT projects, so his general experience and intuition is very important for me. It means, we both gain a lot from this kind of cooperation. Fun of development is also an important factor here :-) From the remote pair programming sessions we've had so far I can say that I'm also very glad of our efficiency.

The tools we use are nothing new. We use vim + screen + skype. We both login to a development server as the same user. There is a screen session already open, so we just 'screen -x' to it, and now we see the same things at the same time. It's fun to watch, when Marcin is typing something, explaining it using skype, and I see it immediately in my vim. At any point, I can grab the keyboard and just start typing. Cool! I can't imagine working like that without a skype session, though. Using IM is just too slow.

I'm strongly recommending this kind of cooperation. I'm aware of the limitations here. The application must be possible to build on a Linux/Unix machine, but even if it can't, I can imagine some work-arounds to this problem. Different timezones might be an issue here. I don't think the whole project must be built like that. However, planning some remote pair programming sessions may increase the quality of your software.

I like this way of working so much, that I even consider doing it more often. Would anyone be interested in a Rails coaching session like that? All you need is skype and an SSH client.

The articles I found useful when preparing to remote pair programming:

http://blog.lathi.net/articles/2007/10/09/remote-pair-programming

http://mikeburnscoder.wordpress.com/2007/06/21/my-rails-development-environment-version-1-vim-and-screen/

Andrzej's Rails tips #2

Changing month names in date_select

Sometimes you may need to change the month names that appear in the date_select (or anywhere in your application). One way of doing it is the following (setting Polish months names):

class Date
MONTHNAMES = %w{Styczeń Luty Marzec Kwiecień
Maj Czerwiec Lipiec Sierpień
Wrzesień Październik Listopad Grudzień}
end
RSpec Stories, steps with parameters

There is an easy way of reusing steps in RSpec stories.
Let's say you had a following step:


Given a search for chess


which was implemented with a hardcoded value like that:

Given "a search for chess" do
@search_term = 'chess'
end


And now you'd like to add another, very similar scenario:

Given a search for bridge

You can reuse the previously implemented step by changing the implementation to:

Given "a search for $term" do |term|
@search_term = term
end


Thanks to that, you can now reuse this step in as many scenarios as you want.

You can find a nice explanation of RSpec stories on Edd Dumbill's blog.

Monday, February 4, 2008

Andrzej's Rails tips #1

has_many :through, :uniq=>true

It's quite common that when you need to use has_many :through, you actually want to see unique objects. Today, I had a need to display a list of producers based on an order.

In my design I have:

Order.has_many :line_items
LineItem.belongs_to :producer
Order.has_many :producers,
:through => :line_items

With this in place, if an order has two products, each of them belonging to the same producer, then order.producers lists the same producer twice.
Adding :uniq => true solves the problem:

Order.has_many :producers,
:through => :line_items,
:uniq => true


More information about it on John Susser's blog.

RESTful routes and pdf

If you have an action that responds to many formats (html, rss, pdf), you can still use a RESTful route with a 'formatted' prefix:

link_to "PDF format",
formatted_order_path(@order, :pdf)

Found on Railscast #78.

Monday, January 21, 2008

What do I gain from TDD or BDD?

The question what do I gain from doing TDD/BDD is asked quite often. I understand the reasons people ask for that. I have practised TDD since 2001. This post is an explanation of why I TDD/BDD and what I gain from it.

Why do I BDD?

BDD helps me with 3 aspects of software development: Design, Defense, Documentation.
  • Design
    • When I write the specifications(tests) first, I see that my design is simpler (as compared to write the code first) for given requirements, thus better.
      • I know it's not very intuitive, you just have to try it.
    • When you start with tests, your design is more testable.
      • It makes debugging easier.
    • TDD doesn't replace drawing diagrams.
      • It's just an additional design technique.
      • I still enjoy drawing UML-like diagrams!
  • Defense
    • I feel much safer having my application covered with tests, .
      • Thanks to that, I can do some refactoring when I feel it's needed, run the tests, and if it's 'green' I just check it in.
    • Whenever some of my users find any bug, I prepare a test that covers it.
      • Only then I fix the bug and release the change.
      • There's nothing worse than having a bug that reappears again (after fixing it) in the application.
  • Documentation
    • I'm not a fan of big Word documents that describe all details of the requirements.
      • It's just too difficult to synchronize this document with reality after every requirements change
        • Changes are inevitable.
      • There was one large project I was working on, where nobody from the team (including the project manager) knew what all the requirements were!
        • Yes, it is pathological.
    • I store all of the requirements as executable stories/tests.
      • Before releasing any change I run all the stories.
      • When there is a requirement change I just find the story responsible for that, change it appropriately and change the code.
        • Changes are inevitable.
Do I have to TDD/BDD in order to be successful?

Well... No.

I know some very good developers that don't write any tests and are successful. There are other ways of ensuring good design or documentation. As for defense, you may have a very fast testing department that makes sure you didn't break anything. Or maybe you just prefer to manually go through your application. For me it's just too slow,
I prefer an immediate response that is free of any human mistakes. Chances are, you are just so perfect that you don't introduce any breaking changes. Lucky you!

It's also possible that you separate the analysis, design and implementation phases so strongly, that there is no way of changing anything during the implementation phase. I'm not a big fan of this approach but I know that many people are successful with that. I believe that even in this case you can gain a lot from writing acceptance tests (executable stories). Writing unit tests for your classes is also a good thing. I'll try to cover the topic of testing on different levels (acceptance, units) in a separate post.


But it's difficult and expensive, isn't it?

Yes, it is. All I gain doesn't come for free.

First of all, it requires discipline. It takes some time to get used to write tests first.
Also, it takes some time to learn how to do correct TDD/BDD. The good news is that you can start introducing it gradually.

Another thing is that after creating a lot of tests for your applications the whole suite may take some time to run. It's a problem that is not easy to solve, but believe me you'd prefer this kind of problems than a 5-days debugging session.

Is it fun?

Yes, it's a lot of fun!
Again, you have to try it before you know, what I'm talking about. If you're still new to TDD, look around, maybe there is someone experienced nearby who can show you a quick demo.

What next?

Find some TDD tools for your technology. I believe they exist for every programming language. Find a place in your application where you'd like to start testing. Start with something simple. Maybe assuring that a request to a given page is successful? Or maybe asserting that all the values in the Order class are calculated correctly? Make sure you run those tests after every change of your application.

Feel free to ask me questions about TDD or BDD. I have practised it with Java, C#, IronPython and Ruby. The technology doesn't matter too much in this case. You can use comments for that or simply email me at andrzejkrzywda at gmail.com.

Thursday, January 17, 2008

RSpec User Story example

Today, just a quick example showing what kind of wonderful things you can do with the new RSpec and its support for executable User Stories, a feature I was dreaming about for years...

Specification as a user story:



Story: Creating an order

Scenario: admin user creates an order
Given an admin user
And some orders
And some customers

When he creates an order

Then a new order is created
And a new customer is created

Implementation of the user story



Given "an admin user" do
login_as_admin
end

Given "some orders" do
@orders_count = Order.count
end

Given "some customers" do
@customers_count = Customer.count
end

When "he creates an order" do
post '/orders/create',
"order"=>{"address_attributes"=> {"name"=>"Customer 1"}}
end

Then "a new order is created" do
Order.count.should == @orders_count + 1
end

Then "a new customer is created" do
Customer.count.should == @customers_count + 1
end

The output when the user story is run:



Running 1 scenarios

Story: Creating an order

Scenario: admin user creates an order

Given an admin user
And some orders
And some customers

When he creates an order

Then a new order is created
And a new customer is created

1 scenarios: 1 succeeded, 0 failed, 0 pending

Tuesday, January 15, 2008

5 reasons to try Resolver One

Disclaimer: I am a Resolver employee, have helped develop it and think it is extraordinarily cool.
  • 1. It's an amazing combination of a spreadsheet application and an IronPython IDE.
    • Resolver One shows a typical spreadsheet grid and a code box.
      • Whatever you do with the grid is reflected in a generated code.
      • In the code box you can write some code which manipulates the workbook object, all the changes are then displayed in the grid.
    • Resolver One is now 1.0 !
  • 2. You can create maintainable spreadsheets.
    • Thanks to the two-ways synchronization a spreadsheet is actually a Python program.
    • Thus, you can do everything that you can do with a program.
      • Create modules, classes, methods.
  • 3. It's probably the first time you can actually unit test your spreadsheet logic and furthermore do some refactoring on it!
  • 4. Because now creating a spreadsheet is limited only by your imagination.
  • 5. It's easy to import your existing spreadsheet, script it with Python and export it back.
Are you convinced now?

Then go and grab a copy of Resolver One
Oh, and did I mention it's free for non-commercial use?

More resources:

resolversystems.com
resolverhacks.net

Friday, January 4, 2008

A Guide to Deploying Rails Applications

This article describes the way I deploy my Rails applications.

Thanks to Ruby on Rails I can quickly help my customers. The speed of development with Rails and agile practices make my work really fast. The only time when I slow down a bit is when I create and deploy a new project. If I saved the time somehow I could spend this time solving business problems instead.
I decided to start with a documentation of my deployment/development process. Then, I automated and simplified all I could and had time to. The result is a semi-automatic process which helps me with creating applications. This process is specialised for my needs and it uses the tools that I found useful. It doesn't have to be good for your needs.
All the setup was focused on agility.
I love to check-in frequently and be able to see the changes immediately on the production server. That's why I use tools like Capistrano or Vlad.

A quick description of the tools I use:
  • Rails
    • I don't think I have to explain why :)
    • most of the time I use edge Rails
  • Mongrel
    • A standard way of deploying in the Rails world.
    • fast and stable
    • Easy to configure
  • nginx
    • an http server that is responsible for load balancing
    • easy to configure (even easier if you can read Russian blogs)
    • easy to handle many Rails applications.
  • Capistrano
    • A standard tool for easy deployment.
    • I use Vlad for some of my applications, both are good.
  • Piston
    • Easy plugins management
  • Subversion
  • mysql
Nginx configuration may be tricky when you work on a shared hosting but the main points stay the same also with Apache and other Http servers. Some of my applications are still on a shared machine (I recommend Webfaction), but most of them are on a dedicated machine.

Let's assume that you want to create and deploy a new Rails applications. The whole process can be described in 5 general steps.
  1. Create an SVN repository, create the app and import it to the repository.
  2. Use capistrano to make the remote work easier.
  3. Create the production database.
  4. Prepare mongrel_cluster to work with the new application.
  5. Configure nginx and bind the domain.

Ok, let's go into details.
  • Remote: Create a repository for the application.
    • svnadmin create /var/repos/app
      • I prefer to have a separate repo for each of my applications.
      • It's good to have one directory for all repositories
      • I use svn+ssh protocol, in my case it's easier to have just one source of users (Linux users)
  • Checkout the application to the /tmp/app directory
    • svn co $REMOTE_SVN/app /tmp/app
      • I recommend having environment variables for the svn paths, works nicely with shell autocompletion.
  • Use edge rails to create a rails app.
    • svn up ~/tmp/rails_edge
      • update it first
      • my rails_edge lives there, you can always create it with 'svn co $RAILS_DEV_TRUNK'
    • ruby ~/tmp/rails_edge/railities/bin/rails -dmysql /tmp/app/trunk
      • '-dmysql' because Rails has recently changed its default db to sqlite
    • svn ci -m "initial import"
  • Live on Rails edge
    • piston import $RAILS_DEV_TRUNK vendor/rails
    • svn ci -m "piston imported edge rails"
  • capify
    • capify .
    • It creates two files.
  • modify config/deploy.rb:

set :application, "app"
my_server = "12.34.56.7"
set :repository, "svn+ssh://#{my_server}/var/repos/app/trunk"
set :deploy_to, "/var/www/#{application}"
role :app, my_server
role :web, my_server
role :db, my_server, :primary => true

  • Commit.
    • svn ci -m "capistrano setup"
  • cap deploy:setup
    • it creates all the directories on the remote server
  • cap deploy:cold
    • it checkouts the code, creates a 'current' symlink
    • logs are in the 'shared' directory
    • it fails on db:migrate task which is fine for now.
  • Create the production database
    • Remote: Go to /var/www/app/current
    • Remote: rake RAILS_ENV=production db:create
  • Local: cap deploy:cold
    • Again.
    • It should pass the db:migrate now
    • But it fails on missing script/spin file
  • Create script/spin with the following content:
    • mongrel_cluster_ctl restart
    • Yes, you need mongrel_cluster on the remote server
    • Add this file to svn and commit
  • cap deploy:cold
    • This time it worked!
    • But... we didn't setup mongrel_cluster to restart our app.
  • Mongrel_cluster
    • Create config/mongrel_cluster.yml
---
cwd: /var/www/app/current
log_file: log/mongrel.log
port: 5000
environment: production
group: www-data
user: your-username
address: 127.0.0.1
pid_file: tmp/pids/mongrel.pid
servers: 3
  • Commit this file
  • cap deploy:cold
    • still doesn't work
    • Mongrel_cluster doesn't know that it should use this file
  • Remote: Go to /etc/mongrel_cluster/
  • Make a symbolic link to the yml file
    • sudo ln -s /var/www/app/current/config/mongrel_cluster.yml app.yml
  • cap deploy:cold
    • And it works!
    • you should now be able to connect to localhost:5000 from the remote server.
  • Now we will bind the domain to this mongrel_cluster.
    • We need to configure nginx.
    • I have setup nginx so that for every application I create a single file that is automatically loaded on nginx startup.
    • The file just needs to live in the /etc/nginx/vhosts/ directory.
    • It works because I have the following line in my /etc/nginx/nginx.conf file:
      • include /etc/nginx/vhosts/*.conf;
  • Create /etc/nginx/vhosts/app.conf

upstream app {
server 127.0.0.1:5000;
server 127.0.0.1:5001;
server 127.0.0.1:5002;
}
server {
listen 80;
client_max_body_size 50M;
server_name app.com www.app.com;
root /var/www/app/current/public;
access_log /var/www/app/current/log/nginx.log;

if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html last;
break;
}

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;
proxy_max_temp_file_size 0;
if (-f $request_filename) {
break;
}
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://app;
break;
}
}

error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/app/current/public;
}
}

  • Restart nginx
    • sudo /etc/init.d/nginx stop
    • sudo /etc/init.d/nginx start
  • That's it :)
  • From now on you just write the code, commit and call capistrano to deploy.

I hope you found this guide useful. There is still a lot to improve and a lot of duplicates (like defining mongrel ports). Let me know if you think that I could do something in a better way.

Here is a list of articles that I found useful when I was experimenting with Rails deployment.

Installing and Configuring Nginx and Mongrel for Rails

Install Ruby Rails on Gutsy Gibbon (Nginx Version)


Hosting Rails apps using nginx and Mongrel Cluster

A clean slate, Edge Rails recipe

Thursday, January 3, 2008

Ruby, pipes and RMagick

I wanted to be able to run the following:

ls *.jpg | grep -v small | ruby thumbnailise.rb

This command lists all jpg files, excludes the ones that were already converted (thumbnailisied) and calls a Ruby script passing the files using a pipeline.
In order to do that I had to find a way of using Unix pipelines in Ruby. The solution is using STDIN.readlines:

require 'RMagick'
require 'pathname'

input = STDIN.readlines
input.each do |line|
filename = line.strip
image = Magick::Image.read(filename).first
image.crop_resized!(100, 100, Magick::NorthGravity)
path = Pathname.new(filename)
outFile = "#{path.basename(".jpg")}_small#{path.extname}"
image.write(outFile)
end


Do you know any nicer solutions how to do that?