Thursday, December 9, 2010

Create Ajax based CRUD using Rails 3

Ajax is well know for handling asynchronous request and Rails is for fast web development. Here I am going to create an application using Rails 3.0.1 which uses Ajax calls for CRUD operations.

1. Create the project


Crate the project address_book. Here I am using sqlite3 to store entries in address book
rails new address_book -d sqlite3

Change directory
cd address_book

Generate scaffold for address book’s Entry.
rails g scaffold Entry name:string address:text phone:string email:string

Now the skeleton for Entry has been created. Use ‘rake’ command to create necessary tables.
rake db:migrate

2. Change entries controller


As we are creating Ajax based CRUD, the application should respond to ‘.js’ format.
respond_to do |format|
format.html
format.js
end

Also we need to modify actions to handle CRUD Ajax requests. Modified entries controller(app/controllers/entries_controller.rb) looks like:
class EntriesController < ApplicationController
def index
@entry = Entry.new
@entries = Entry.all

respond_to do |format|
format.html
format.js
end
end

def show
@entry = Entry.find(params[:id])

respond_to do |format|
format.html
format.js
end
end

def new
@entry = Entry.new

respond_to do |format|
format.html
format.js
end
end

def edit
@entry = Entry.find(params[:id])
respond_to do |format|
format.html
format.js
end
end

def create
@entry = Entry.new(params[:entry])

respond_to do |format|
if @entry.save
format.html { redirect_to(@entry) }
format.js
else
format.html { render :action => "new" }
end
end
end

def update
@entry = Entry.find(params[:id])

respond_to do |format|
if @entry.update_attributes(params[:entry])
format.html { redirect_to(@entry) }
format.js
else
format.html { render :action => "edit" }
end
end
end

def destroy
@entry = Entry.find(params[:id])
@entry.destroy

respond_to do |format|
format.html { redirect_to(entries_url) }
format.js
end
end
end

3. Change Entry model


Define attributes in Entry model to access fields of entries table. Also put some validations on its fields.

Here is the modified Entry model(app/models/entry.rb)
class Entry < ActiveRecord::Base
attr_accessible :name, :address, :phone, :email
validates_presence_of :name, :phone, :email
end

4. Change in views


Now we modify index page(app/views/entries/index.html.erb) to show Entry form.
<h1>Listing entries</h1>

<table id="entries">
<tr>
<th>Name</th>
<th>Address</th>
<th>Phone</th>
<th>Email</th>
<th></th>
<th></th>
<th></th>
</tr>

<% @entries.each do |entry| %>
<%= render entry %>
<% end %>
</table>

<br />

<h2>Entry form</h2>
<div id="form">
<%= render :partial => "form" %>
</div>

In entries's index page you can see that we used id 'entries' with listing table, it is used to manipulate DOM object using jQuery.

Change form partial(app/views/entries/_form.html.erb) to generate remote POST request.
<%= form_for(@entry, :remote => true) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :address %><br />
<%= f.text_area :address, :rows => 3 %>
</div>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

Create Entry partial(app/views/entries/_entry.html.erb) to list address book’s Entry.
Before going ahead, we should take care of two things: one is to update index page after receiving response of the Ajax request, and another is to send Ajax request to edit and destroy Entry.

Here we are updating index page by manipulating DOM object using jQuery. But, to manipulate DOM object, each row should be uniquely identified. So, we define unique 'id' attribute with each row.

To send Ajax request for editing and deleting Entry, use ':remote => true' parameter with Edit and Destroy link.

<tr id="<%= dom_id entry %>">
<td><%= entry.name %></td>
<td><%= entry.address %></td>
<td><%= entry.phone %></td>
<td><%= entry.email %></td>
<td><%= link_to 'Show', entry %></td>
<td><%= link_to 'Edit', edit_entry_path(entry), :remote => true %></td>
<td><%= link_to 'Destroy', entry, :confirm => 'Are you sure?', :method => :delete, :remote => true %></td>
</tr>

Now, write javascript templates to update index page.

Create app/views/entries/create.js.erb template to update the list on adding new Entry and clear the form.
$('<%= escape_javascript(render(:partial => @entry))%>').appendTo('#entries');
$("#new_entry")[0].reset();

Create app/views/entries/edit.js.erb template to set values into the form.
$("#form > form").replaceWith("<%= escape_javascript(render(:partial => "form"))%>")

Create app/views/entries/update.js.erb template to update the list with updated Entry, create new Entry and clear the form.
$("#<%= dom_id(@entry) %>").replaceWith("<%= escape_javascript(render(:partial => @entry)) %>");
<% @entry = Entry.new # reset for new form %>
$(".edit_entry").replaceWith("<%= escape_javascript(render(:partial => "form"))%>")
$(".new_entry")[0].reset();

Create app/views/entries/destroy.js.erb template to delete Entry from the list.
$('#<%= dom_id @entry %>').remove();

Modify application layout (app/views/layouts/application.html.erb) to include ‘rails’ and ‘application’ javascript.
<!DOCTYPE html>
<html>
<head>
<title>AddressBook</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>

<%= yield %>

<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" %>
<%= javascript_include_tag "rails" %>
<%= javascript_include_tag "application" %>

</body>
</html>

5. Replace ‘rails.js’



Replace rails javascript(public/javascripts/rails.js) with JQuery.
jQuery(function ($) {
var csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content');

$.fn.extend({
/**
* Triggers a custom event on an element and returns the event result
* this is used to get around not being able to ensure callbacks are placed
* at the end of the chain.
*
* TODO: deprecate with jQuery 1.4.2 release, in favor of subscribing to our
* own events and placing ourselves at the end of the chain.
*/
triggerAndReturn: function (name, data) {
var event = new $.Event(name);
this.trigger(event, data);

return event.result !== false;
},

/**
* Handles execution of remote calls firing overridable events along the way
*/
callRemote: function () {
var el = this,
data = el.is('form') ? el.serializeArray() : [],
method = el.attr('method') || el.attr('data-method') || 'GET',
url = el.attr('action') || el.attr('href');

if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
} else {
if (el.triggerAndReturn('ajax:before')) {
$.ajax({
url: url,
data: data,
dataType: 'script',
type: method.toUpperCase(),
beforeSend: function (xhr) {
el.trigger('ajax:loading', xhr);
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
},
complete: function (xhr) {
el.trigger('ajax:complete', xhr);
},
error: function (xhr, status, error) {
el.trigger('ajax:failure', [xhr, status, error]);
}
});
}

el.trigger('ajax:after');
}
}
});

/**
* confirmation handler
*/
$('a[data-confirm],input[data-confirm]').live('click', function () {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
if (!confirm(el.attr('data-confirm'))) {
return false;
}
}
});


/**
* remote handlers
*/
$('form[data-remote]').live('submit', function (e) {
$(this).callRemote();
e.preventDefault();
});

$('a[data-remote],input[data-remote]').live('click', function (e) {
$(this).callRemote();
e.preventDefault();
});

$('a[data-method]:not([data-remote])').live('click', function (e){
var link = $(this),
href = link.attr('href'),
method = link.attr('data-method'),
form = $('<form method="post" action="'+href+'"></form>'),
metadata_input = '<input name="_method" value="'+method+'" type="hidden" />';

if (csrf_param != null && csrf_token != null) {
metadata_input += '<input name="'+csrf_param+'" value="'+csrf_token+'" type="hidden" />';
}

form.hide()
.append(metadata_input)
.appendTo('body');

e.preventDefault();
form.submit();
});

/**
* disable-with handlers
*/
var disable_with_input_selector = 'input[data-disable-with]';
var disable_with_form_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')';

$(disable_with_form_selector).live('ajax:before', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.data('enable-with', input.val())
.attr('value', input.attr('data-disable-with'))
.attr('disabled', 'disabled');
});
});

$(disable_with_form_selector).live('ajax:after', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.removeAttr('disabled')
.val(input.data('enable-with'));
});
});
});

6. Set index page


To set entries index page as the application’s index page, set root entry into config/routes.rb file.
AddressBook::Application.routes.draw do
resources :entries
root :to => "entries#index"

Remove static index page from public folder.
rm public/index.html

Now your application is ready to run.

Tuesday, October 12, 2010

Uninstall all Ruby gems

Some time you may need to uninstall all the installed Ruby gems. Uninstalling individual gems is tedious job. Here you will find how to uninstall all the installed Ruby gems using single command.

First, we need a list of installed gems. Its easier to get list of installed gems by using the command:

gem list --no-version

The command to uninstall all the matching version of gems without any confirmation of dependencies and executables is:

gem uninstall -aIx GEM_NAME

Now, we use xargs command to uninstall all the installed gems:

gem list --no-version | xargs gem uninstall -aIx

If your system requires sudo privilege to uninstall gems use:

gem list –no-version | sudo xargs gem uninstall -aIxM

Thursday, September 23, 2010

Create TCP Echo Server using Libev

Libev provides APIs to write asynchronous socket programming. This article describes how to write a simple TCP echo server using libev.

Lets start to write simple asynchronous TCP echo server with the help of libev. The server accepts client connections, reads messages from connections and echo the same message back.

Steps to write TCP echo server:
  1. Create a server socket and bind to socket address
  2. Listen on server socket
  3. Create watcher to accept connection
  4. Write connection accept callback function
  5. Create and initialize watcher to read message from client
  6. Write callback function to read message
  7. Start event loop

1. Create a server socket and bind to socket address

First step is to create TCP internet socket and bind it to some port.
sd = socket(PF_INET, SOCK_STREAM, 0);

bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NO);
addr.sin_addr.s_addr = INADDR_ANY;

bind(sd, (struct sockaddr*) &addr, sizeof(addr));

2. Listen on server socket

To accept client connection, you need to start listing on server socket. Here we are using backlog of size 2.
listen(sd, 2);

3. Create watcher to accept connection

Now we accept client connection using 'accept' API. Before that, we create a watcher to keep watch on accept call. Initialize the watcher using server socket and connection accept callback function.
ev_io_init(&w_accept, accept_cb, sd, EV_READ);
ev_io_start(loop, &w_accept);

4. Write connection accept callback function

Write appropriate callback function to accept client connection.
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
...
client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
...
}

5. Create and initialize watcher to read message from client

A new client socket is created on accepting the connection. To read messages from client, create a new watcher. Initialize it with client socket and read callback function.

w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
...
ev_io_init(w_client, read_cb, client_sd, EV_READ);
ev_io_start(loop, w_client);

6. Write callback function to read message

Write appropriate read callback function to receive message from client and echo the same message back.
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
...
read = recv(watcher->fd, buffer, BUFFER_SIZE, 0);
send(watcher->fd, buffer, read, 0);
...
}

7. Start event loop

Final step is to run infinite event loop to wait for events.

while (1)
{
ev_loop(loop, 0);
}

Now we have done with server code.

Here is the complete code for the server.

#include <stdio.h>
#include <netinet/in.h>
#include <ev.h>

#define PORT_NO 3033
#define BUFFER_SIZE 1024

int total_clients = 0; // Total number of connected clients

void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents);
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents);

int main()
{
struct ev_loop *loop = ev_default_loop(0);
int sd;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
struct ev_io w_accept;

// Create server socket
if( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
{
perror("socket error");
return -1;
}

bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NO);
addr.sin_addr.s_addr = INADDR_ANY;

// Bind socket to address
if (bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
{
perror("bind error");
}

// Start listing on the socket
if (listen(sd, 2) < 0)
{
perror("listen error");
return -1;
}

// Initialize and start a watcher to accepts client requests
ev_io_init(&w_accept, accept_cb, sd, EV_READ);
ev_io_start(loop, &w_accept);

// Start infinite loop
while (1)
{
ev_loop(loop, 0);
}

return 0;
}

/* Accept client requests */
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sd;
struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));

if(EV_ERROR & revents)
{
perror("got invalid event");
return;
}

// Accept client request
client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);

if (client_sd < 0)
{
perror("accept error");
return;
}

total_clients ++; // Increment total_clients count
printf("Successfully connected with client.\n");
printf("%d client(s) connected.\n", total_clients);

// Initialize and start watcher to read client requests
ev_io_init(w_client, read_cb, client_sd, EV_READ);
ev_io_start(loop, w_client);
}

/* Read client message */
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents){
char buffer[BUFFER_SIZE];
ssize_t read;

if(EV_ERROR & revents)
{
perror("got invalid event");
return;
}

// Receive message from client socket
read = recv(watcher->fd, buffer, BUFFER_SIZE, 0);

if(read < 0)
{
perror("read error");
return;
}

if(read == 0)
{
// Stop and free watchet if client socket is closing
ev_io_stop(loop,watcher);
free(watcher);
perror("peer might closing");
total_clients --; // Decrement total_clients count
printf("%d client(s) connected.\n", total_clients);
return;
}
else
{
printf("message:%s\n",buffer);
}

// Send message bach to the client
send(watcher->fd, buffer, read, 0);
bzero(buffer, read);
}


Below is the complete code for basic client which connect to server and, send and receive message. You can also write your own client.
#include <stdio.h>
#include <netinet/in.h>

#define PORT_NO 3033
#define BUFFER_SIZE 1024

int main()
{
int sd;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
char buffer[BUFFER_SIZE] = "";

// Create client socket
if( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
{
perror("socket error");
return -1;
}

bzero(&addr, sizeof(addr));

addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NO);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

// Connect to server socket
if(connect(sd, (struct sockaddr *)&addr, sizeof addr) < 0)
{
perror("Connect error");
return -1;
}

while (strcmp(buffer, "q") != 0)
{
// Read input from user and send message to the server
gets(buffer);
send(sd, buffer, strlen(buffer), 0);

// Receive message from the server
recv(sd, buffer, BUFFER_SIZE, 0);
printf("message: %s\n", buffer);
}

return 0;
}