Home > Apache, MySQL, Ruby on Rails, web development > Developing Web Based POS Application Using Ruby on Rails on Debian 5

Developing Web Based POS Application Using Ruby on Rails on Debian 5

September 7th, 2010 Leave a comment Go to comments

In this article we will learn how to begin developing web applications using Ruby on Rails. First we setup the development environment, then develop a simple CRUD (create, read, update, delete) application using MySQL database, and deploy the application to Apache web server. An an example, we will develop a very simple point of sales (POS) application that consists only 3 tables: orders, order_details, and items.

 

Ruby on Rails Installation

Install Ruby if it’s not installed yet on your system:

# apt-get install  ruby1.8 ruby1.8-dev

 

Install Ruby Gem package management directly from Rubyforge since we need the newer version than version 1.3.1 that is shipped with Debian 5:


# wget http://rubyforge.org/frs/download.php/70696/rubygems-1.3.7.tgz

# tar xfz rubygems-1.3.7.tgz

# cd rubygems-1.3.7.tgz

# ruby setup.rb

# ln -s /usr/bin/gem1.8 /usr/bin/gem

 

Install Ruby on rails version 2.3.8 using gem command:

# gem install -v=2.3.8 rails

 

 

Start Developing the Application

We are going to put our application on /opt/rubyapp or whatever directory you like.

# cd /opt

# mkdir rubyapp

 

On that directory, run rails command to create a new application skeleton. We are going to create a simple POS application and we call it simplepos.

# rails simplepos

 

Rails will create the application skeleton on the directory name we specified. Go to that directory and run the server.

# cd simplepos

# ruby script/server

 

Ruby on Rails server will start and we can access it from http://yourserver-ip:3000, like the following:

image

 

Note: I once found a conflicting port 3000 on Debian 5, which was used by Dr WEB application. Simply stop that service or change the port in order that Rails application to start correctly.

You can see that the default database driver is SQL lite. We need to change this to MySQL. Edit the config/database.yml:

development:
  adapter: mysql
  database: simplepos_development
  user: root
  password: 1234
  pool: 5
  timeout: 5000

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: mysql
  database: simplepos_test
  user: root
  password: 1234
  pool: 5
  timeout: 5000

production:
  adapter: mysql
  database: simplepos_production
  user: root
  password: 1234
  pool: 5
  timeout: 5000

Adjust the user name and password according to your system. Then run this command to create all the databases defined on the config file:

# rake db:create:all

 

 

Run the server again (ruby script/server) and make sure that our database is now MySQL:

image

 

The Application Design

Our application is quite simple. It contains only 3 tables

  • orders for storing sales transactions with the following fields:
    • number
    • order_date
  • items for storing items to be sold the following fields:
    • number
    • name
    • description
    • unit_price
  • order_details for storing order details with the following fields:
    • order_id
    • item_id
    • qty
    • unit_price
    • amount

Here is the relation of the 3 tables:

image

 

Creating Model , Controller, and View using Scaffold for Orders Data

Let’s move on to coding. Rails provides us a very powerful scaffolding script that allows us to easily create the model, view, and controller, and even the tables related to the model.

First, create the Order model. Here we follow the Rails convention: model use singular word.

# ruby script/generate scaffold Order number:integer order_date:date

Rails will create our Order model, view, and controller. Take a look on simplepos/app folder. Next create the database table according to the fields that we sepcified on the scaffold above:

# rake db:migrate

Now, run the server again (ruby script/server) and test the CRUD functionality for the Order data by entering the URL address: http://yourserver-ip:3000/orders. We can see the list orf orders on the index page, a new order creation link, along with edit, update, and delete link if there’s an order already.

Listing orders:

image

Adding new order:

image

Order created:

image

List the orders on the order list:

image

Editing the order:

image

Deleting the order:

image

What have we have is a complete CRUD application for Orders table created automatically by Rails scaffolding.

 

Creating Model , Controller, and View using Scaffold for Items Data

Now create the model, controller, view, and table for items data, using the same steps as above.

# ruby script/generate scaffold Item number:integer name:string description:text unit_price:decimal

Then create the database table according to the fields that we specified on the scaffold above:

# rake db:migrate

And try to run the items CRUD by pointing to URL http://yourserver-ip:3000/items . You should be able to list, create, edit, and delete the items data now. This is the example of creating new item form.

image

 

Validating Data

If we need to validate data entered by the user, then we can easily edit the model and add validations to the fields. For example, we need to validate the existence of number and  order_date fields on orders table. Also we need to validate whether total_amount field is a number or not. Edit the app/model/order.rb file and add the following bold lines:

class Order < ActiveRecord::Base
   validates_presence_of :number, :order_date 
end

Stop and start the server again, and let’s enter the wrong data according to the validation rule.

Entering a blank number, order_date.

image

Of course, we could also add the validation to Items data by editing the app/model/item.rb file, by adding the following bold lines:

class Item < ActiveRecord::Base
  
validates_presence_of :number, :name, :unit_price
   validates_numericality_of :unit_price

end

Try to create a blank number, name, unit_price on item data:

image

 

Creating Order Details MVC and Set the Relation

Order details is the place where we stored the actual sales transaction. It will record the items purchased on a particular order number. So it has a relation to items table and orders table as well, both for a “belongs to” relationship. So the relations will be:

  • orders has many order_details
  • items has many order_details
  • order_details belongs to orders and items
    Let’s create the MVC for this order details using the same steps as before:

    # ruby script/generate scaffold OrderDetail order:references item:references qty:integer unit_price:decimal amount:decimal

    Note that we used references data type for order and item on OrderDetail model. This will make Rails to create a relationship between OrderDetail and Order and Item model (ie OrderDetails belongs to Order and Item). It will also create a reference column on the order_details table, which is order_id and items_id.

    Then create the database table according to the fields that we specified on the scaffold above:

    # rake db:migrate

     

    And take a look at the generated order_details table:

mysql> explain order_details;
+————+—————+——+—–+———+—————-+
| Field      | Type          | Null | Key | Default | Extra          |
+————+—————+——+—–+———+—————-+
| id         | int(11)       | NO   | PRI | NULL    | auto_increment |
| order_id   | int(11)       | YES  |     | NULL    |                |
| item_id    | int(11)       | YES  |     | NULL    |
                |
| qty        | int(11)       | YES  |     | NULL    |                |
| unit_price | decimal(10,0) | YES  |     | NULL    |                |
| amount     | decimal(10,0) | YES  |     | NULL    |                |
| created_at | datetime      | YES  |     | NULL    |                |
| updated_at | datetime      | YES  |     | NULL    |                |
+————+—————+——+—–+———+—————-+
8 rows in set (0.00 sec)

 

Note: created_at and updated_at are auto generated columns when we create the scaffolding.

Let’s define this relations to the models. First edit the order model (app/model/order.rb) and add the following bold lines:

class Order < ActiveRecord::Base
   validates_presence_of :number, :order_date
  
has_many :order_details
end

And to the item model (app/model/item.rb)

class Item < ActiveRecord::Base
   validates_presence_of :number, :name, :unit_price
   validates_numericality_of :unit_price
   has_many :order_details
end

Also check the order detail model (app/model/order_detail.rb) has the relationship already because we specify that order and items are references when do the scaffolding.

class OrderDetail < ActiveRecord::Base
  belongs_to :order
  belongs_to :item
end

 

Edit routes.rb

We also need to edit the config/routes.rb so that the URL for order and order details also have a relationship information that order has many order_details. Modify the file so it looks like the following:

ActionController::Routing::Routes.draw do |map|
  map.resources :items

  map.resources :orders, :has_many => :order_details

….

It will enable us to access the order details data below the order URL like this: http://yourserver:3000/orders/1/order_detail

 

Adding Order Detail Form

We need to add the detail form below the Order show page so that we can add item details for a particular Order.

Add these bold lines just below the Date field of the Order:

<p>
  <b>Order date:</b>
  <%=h @order.order_date %>
</p>

<h1>Order details</h1>
<table>
<tr>
   <th>Item</th>
   <th>Qty</th>
   <th>Unit Price</th>
   <th>Amount</th>
</tr>

<%= render :partial => @order.order_details %>

<% form_for [@order, OrderDetail.new] do |f| %>
<tr>
   <td><%= f.label :item_id, "item" %></td>
   <td><%= f.label :qty, "Qty" %></td>
</tr>
<tr>
   <td><%= f.select :item_id, @items.map {|u| [u.name,u.id %></td>
   <td><%= f.text_field :qty %></td>
</tr>
<tr>
   <td></td><td><%= f.submit "add detail" %></td>
</tr>
<% end %>
</table>

...

 

We need to create a new file under app/views/order_details, called app/views/order_details/_order_detail.html.erb. This file is required by the :render=>partial line above and is a partial template file used to render each order detail lines for an Order.

 

<% div_for order_detail do %>
<tr>
   <td><%=h order_detail.item.name %></td>
   <td><%=h order_detail.qty %></td>
   <td><%=h order_detail.unit_price %></td>
   <td><%=h order_detail.amount %></td>
</tr>
<% end %>

 

Then, we create a form for order details of an order ( form_for [@order, OrderDetail.new] do |f| ). After this, f variable will be related to order_details, but when submitted it will go to  /orders/<id>/order_details.

On that form, we use select() helper function to render a selection box that is populated with all items data that we have where users can pick an item from. Using @items.map we create an array that is required by the select() as its option values.

Next, edit app/controller/orders_controller.rb file so that it provides us with @items object that we used on the select() above.

def show
  @order = Order.find(params[:id])
  @items = Item.find(:all)

  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :xml => @order }
  end
end

 

Modify app/controller/order_details_controller.rb so that it contains only one create method (delete all other methods generated by the scaffolding process). This is invoked when we create a new order_detail record.

 

class OrderDetailsController < ApplicationController
   def create
      @order = Order.find(params[:order_id])
      @order_detail = @order.order_details.create!(params[:order_detail])
      item = Item.find(params[:order_detail][:item_id])
      qty = params[:order_detail][:qty]
      @order_detail.update_attributes(:amount=>item.unit_price.to_i * qty.to_i , :unit_price=>item.unit_price)
      redirect_to @order
   end
end

 

On this method, first we find the order identified by an ID (params[:order_id]) which is a parameter posted by the order detail form.

Found the Order, we create an OrderDetail record by calling @order.order_details.create!() method. This is possible because we have declared that an order can have many order_details.

Next we find the item that is being selected by the user, to find out its unit_price and calculate the order detail amount. Then we update the attributes of order_details using these values.

After all processes are done, we redirect back to the order URL (redirect_to @order).

 

It’s time now to try our modified application, go to Order URL and click Show link. We will have a new look of the form:

image

Try to add items on the order and check the unit price and amount.

 

Calculating Order’s Total Amount

It’s time to calculate the total amount of an order which is the sum of amounts of all order_details that it has.

This is easily calculated using the relation that we have set before (order has many order_details). After defining that relation, we will be able to call order.order_details.sum(colname) and get the total value of colname on the order_details table where order_id is the order that we are looking at.

So, we can edit add/model/order.rb to add a new method total_amount that will return the sum of column amount on the table that it has many (order_details):

class Order < ActiveRecord::Base
   validates_presence_of :number, :order_date, :total_amount
   validates_numericality_of :total_amount
   has_many :order_details

   def total_amount
      self.order_details.sum(:amount)
   end
end

Now we can use that method from our views. Edit the app/views/orders/index.html.erb:

<h1>Listing orders</h1>

<table>
  <tr>
    <th>Number</th>
    <th>Order date</th>
    <th>Total amount</th>
  </tr>

<% @orders.each do |order| %>
  <tr>
    <td><%=h order.number %></td>
    <td><%=h order.order_date %></td>
    <td><%=h order.total_amount %></td>
    <td><%= link_to ‘Show’, order %></td>
    <td><%= link_to ‘Edit’, edit_order_path(order) %></td>
    <td><%= link_to ‘Destroy’, order, :confirm => ‘Are you sure?’, :method => :delete %></td>
  </tr>
<% end %>
</table>

Here is the look of the modified index pages:

image

And also the app/views/orders/show.html.erb:

<p>
  <b>Number:</b>
  <%=h @order.number %>
</p>

<p>
  <b>Order date:</b>
  <%=h @order.order_date %>
</p>

<p>
  <b>Total amount:</b>
  <%=h @order.total_amount %>
</p>

. . .

Here is the look of the modified show page:

image

 

 

Decorating and Layout

 

Per model layout

As part of the scaffolding process, Rails generates a layout template for each model that we created. It is located on app/views/layouts directory. For example, we change the layout of the Items data, which is the app/views/layouts/items.html.erb.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Items: <%= controller.action_name %></title>
  <%= stylesheet_link_tag ‘scaffold’ %>
</head>
<body>

<p style="color: green"><%= notice %></p>

<%= yield %>

</body>
</html>

As you can see, it’s a regular HTML file with a special tag that comes from Rails, which are    <%= stylesheet_link_tag ‘scaffold’ %>  and <%= yield %>. The first tag is how Rails application link a CSS file, in this case scaffold.css. Rails automatically search this file on public/stylesheets directory on our application folder.

The next tag is <%= yield %>. This is a Rails variable that contains the data generated by the view. So everything that we write on the view files will be contained in this variable and rendered on this layout file.

Let’s modify the body background color for Items data only, so change the body tag to <body style="background-color:#aaa">. We should see the background color for Items listing, edit, create, and update page will be changed.

image

Global application layout

If you’d like to change the layout globally for each model pages, then you will need to create a new file called app/views/layouts/application.html.erb, and delete  all other layout files generated by the scaffold. You can copy from another layout file exists as a template, then do the layout adjustments on that file and rename it to application.html.erb.

Having only application.html.erb will make Rails to use that file as the layout for the application. So you have to delete all other layout files, otherwise Rails will use that file instead.

The CSS, image, and Javascript files location

On your application directory, there’s a directory named public. This is where we store all public resources, like CSS, images, and Javascript files.

Rails provides us with some helper functions to generate the proper path for accessing these things, for example image_tag() to access images on public/images, stylesheet_link_tag() to access CSS files, and javascript_include_tag() to access Javascript files.

 

The Default Welcome Page

When pointing our browser to http://yourserver-ip:3000/ we will get the Ruby on Rails welcome page by default. We can change this default behaviour by editing the config/routes.rb file. Look at the map.root :controller => "welcome" line, and uncomment that line.

map.root :controller => "welcome"

 

It means that our application’s default controller is called welcome, which is not exist right now.

Next step, we need to delete public/index.html file.

# rm public/index.html

 

Then, we need to create that controller, simply by  running script/generate again but this time with controller parameter and not scaffold as we have used before.

# ruby script/generate controller welcome index

The command tells Rails to create for us a controller named welcome with a method index. Beside that Rails will also create a view file on app/views/welcome/index.html.erb that we can customize right away.

Let’s modify app/views/welcome/index.html.erb  so that it will show us a main menu for our application :

<h1>Welcome to Simple POS</h1>

<ul>
   <li><%= link_to "Orders" , orders_path %></li>
   <li><%= link_to "Manage Items" , items_path %></li>
</ul>

Here we use another Rails helper function link_to() which will create a HTML <a href> tag, which will display a link text as specified by the first parameter, and will jump to the URL that we specified in it’s second parameter.

We also use orders_path and items_path, which are the helper functions that is automatically generated by the scaffolding process to go to the index page (listing) of the order and item data.

The link can also be referred as these:

   <%= link_to "Orders" , :controller=>"orders", :action=>"index" %>


 

 

Testing the Application

Rails provides us a test suite for testing out our application programmatically. This will make us easier to test all functionality of our application without testing it manually by entering data on the user interface pages.

For example, it we need to test the application functionalities, run this comman:

# rake test:functionals

We can see the test results and  will be notified if something not expected exists.

 

Deploying Into Apache Web Server

Install modrails aka Passenger to be used by Apache web server:

gem install passenger

Then type:

passenger-install-apache2-module

Then follow the instructions on that script.

Deploying on name based virtual host:

Add a virtual host entry to your Apache configuration file. Make sure that the following conditions are met:

  • The virtual host’s document root must point to your Ruby on Rails application’s public folder.
  • The Apache per-directory permissions must allow access to this folder.
  • MultiViews must be disabled for this folder.

For example:

<VirtualHost *:80>
    ServerName www.simpleblog.com
    DocumentRoot /opt/rubyapp/simpleblog/public
    <Directory /opt/rubyapp/simpleblog/public>
        Allow from all
        Options -MultiViews
    </Directory>
</VirtualHost>

You may also need to tweak your file/folder permissions. Make sure that the following folders are readable and executable by Apache:

  • this public folder.

  • the application’s config folder.

  • all parent folders. That is, /opt/rubyapp/simpleblog , /opt and /opt/rubyapp/must also be readable and executable by Apache.

    Then restart Apache. The application has now been deployed.

Deploying to a sub URI:

Suppose that you already have a virtual host:

<VirtualHost *:80>
    ServerName www.yoursite.com
    DocumentRoot /www/yoursite
    <Directory /www/yoursite>
        Allow from all
    </Directory>
</VirtualHost>

And you want your Ruby on Rails application to be accessible from the URL http://www.yoursite.com/simpleblog.

To do this, make a symlink from your Ruby on Rails application’s public folder to a directory in the document root. For example:

ln -s /opt/rubyapp/simpleblog/public /www/yoursite/simpleblog

Next, add a RailsBaseURI option to the virtual host configuration, and also make sure that:

  • The Apache per-directory permissions allow access to this folder.

  • MultiViews is disabled for this folder.

For example (notice the lines in bold):

<VirtualHost *:80>
    ServerName www.yoursite.nl
    DocumentRoot /www/yoursite
    <Directory /www/yoursite>
        Allow from all
    </Directory>

    RailsBaseURI /simpleblog
    <Directory /www/yoursite/simpleblog>
        Options -MultiViews
    </Directory>
</VirtualHost>

Then restart Apache. The application has now been deployed.

 

Deploying to a port based virtual host

What if you want to access your application using a port number, like http://yoursite.com:9000. To do this, use Apache’s port based virtual host.

Add Listen directive to ports.conf or httpd.conf:

Listen 9000

 

 

Then add a virtual host entry to your Apache configuration file. Make sure that the following conditions are met:

  • The virtual host’s document root must point to your Ruby on Rails application’s public folder.
  • The Apache per-directory permissions must allow access to this folder.
  • MultiViews must be disabled for this folder.
    For example:

<VirtualHost *:9000>

   ServerName www.yoursite.com

      DocumentRoot /opt/rubyapp/simpleblog/public

      <Directory /opt/rubyapp/simpleblog/public>

         AllowOverride all

         Options -MultiViews

      </Directory>

</VirtualHost>

      Also, delete or comment out the NameVirtualHost * directive on your httpd.conf.

    Then restart Apache. The application has now been deployed.

     

    Resources

        Akhmad Daniel Sembiring

        akhmad.daniel[at]gmail.com

        vitraining.com -CEO

        • Share/Bookmark
        1. November 18th, 2010 at 18:49 | #1

          Good its very interesting..

        2. December 4th, 2010 at 22:32 | #2

          I cherished what you’ve got got performed here. The format is stylish, your written materials elegant. Nonetheless, you’ve got obtained an edginess to what you will be offering the subsequent. Ill certainly arrive back once more yet once again for a great deal a lot a lot more in case you protect this up. Dont do away with hope if not at the similar time several males and women see your imaginative and prescient vision, know you may have attained a fan suitable the subsequent who beliefs what you might have received to say together with the way you’ve got presented by yourself. Extremely great on you!

        1. No trackbacks yet.
        This site uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.6.0.