Developing Web Based POS Application Using Ruby on Rails on Debian 5
- Ruby on Rails Installation
- Start Developing the Application
- The Application Design
- Creating Model , Controller, and View using Scaffold for Orders Data
- Creating Model , Controller, and View using Scaffold for Items Data
- Validating Data
- Creating Order Details MVC and Set the Relation
- Edit routes.rb
- Adding Order Detail Form
- Calculating Order’s Total Amount
- Decorating and Layout
- The Default Welcome Page
- Testing the Application
- Deploying Into Apache Web Server
- Resources
- Comments (2)
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:
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: 5000production:
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:
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:
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:
Adding new order:
Order created:
List the orders on the order list:
Editing the order:
Deleting the order:
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.
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.
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:
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 :itemsmap.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:
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_detailsdef 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:
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:
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.
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.comDocumentRoot /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



Good its very interesting..
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!