Ruby on Rails CRUD and API tutorial

We organized this Saturday a Ruby on Rails Workshop, where the main goal was everyone to understand the basic principles of Rails, by developing a simple application with basic features: CRUD using MySQL database and a basic API using Grape gem. We thought it will be useful for our readers if we share this simple application and try to better explain by publishing a step by step tutorial.
Creating CRUD is a very common task in WEB development. CRUD is standard for:
- Create
- Read
- Update
- Delete
In this tutorial we will create a Rails CRUD grid, a API CRUD and demonstrate how Rails communicate with a MYSQL database.
So let's get started!
Create a new Rails Application through console:
rails new crud_api
Open the application generated in a IDE, search for Gemfile file and add the following gem:
gem 'grape'
Open config/application.rb and add:
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
Run from console:
bundle install
Open config/database.yml and set your MYSQL settings. Open app/views/layouts/application.html.erb and insert this lines in head:
<%= stylesheet_link_tag 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' %> <%= stylesheet_link_tag 'https://cdn.datatables.net/s/dt/dt-1.10.10,r-2.0.0/datatables.min.css' %>
this lines before </body>
<%= javascript_include_tag 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js' %> <%= javascript_include_tag 'https://cdn.datatables.net/s/dt/dt-1.10.10,r-2.0.0/datatables.min.js' %>
And replace <%= yield %> with this code:
<div class="container"> <div class="row"> <div class="col-md-12"><% if flash[:notice] %> <div class="alert alert-success"><%= flash[:notice] %></div> <% end %> <%= yield %></div> </div> </div>
Go to app/assets/javascripts/application.js and add the javascript code that load the jQuery DataTable:
var ready = function() {
$('#products').DataTable({
"columnDefs": [
{ "width": "19%", className: "dt-body-center", "targets": -1 },
{ "width": "10%", "targets": 0 },
{ "width": "7%", "targets": 1 },
{ "width": "20%", "targets": 2 },
{ "width": "20%", "targets": 3 },
]
});
}
$(document).ready(ready);
$(document).on('page:load', ready);
Create a new controller from command line:
rails g controller products index show new create edit update destroy
Create a new model from command line:
rails g model product name:string price:decimal short_description:text full_description:text
Go to app/controllers/products_controller.rb and write this methods:
class ProductsController < ApplicationController
# GET method to get all products from database
def index
@products = Product.all
end
# GET method to get a product by id
def show
@product = Product.find(params[:id])
end
# GET method for the new product form
def new
@product = Product.new
end
# POST method for processing form data
def create
@product = Product.new(product_params)
if @product.save
flash[:notice] = 'Product added!'
redirect_to root_path
else
flash[:error] = 'Failed to edit product!'
render :new
end
end
# GET method for editing a product based on id
def edit
@product = Product.find(params[:id])
end
# PUT method for updating in database a product based on id
def update
@product = Product.find(params[:id])
if @product.update_attributes(product_params)
flash[:notice] = 'Product updated!'
redirect_to root_path
else
flash[:error] = 'Failed to edit product!'
render :edit
end
end
# DELETE method for deleting a product from database based on id
def destroy
@product = Product.find(params[:id])
if @product.delete
flash[:notice] = 'Product deleted!'
redirect_to root_path
else
flash[:error] = 'Failed to delete this product!'
render :destroy
end
end
# we used strong parameters for the validation of params
def product_params
params.require(:product).permit(:name, :price, :old_price, :short_description, :full_description)
end
end
Open app/models/product.rb and let's make some validations on the name, price and short description fields:
validates :name, presence: true
validates :price, presence: true, numericality: {:greater_than => 0}
validates :short_description, presence: true
Now we have a controller and a model, in order to access the application in browser let's add the routes, so open config/routes.rb and add:
resources :products root 'products#index'
The views you can find on github at:
Now the API part, create a folder named api in app, and inside api create products folder. So the structure will be app/api/products. Here create products_api.rb file and insert this code:
module Products
class ProductsAPI < Grape::API
format :json
desc "Product List", {
:notes => <<-NOTE
Get All Products
__________________
NOTE
}
get do
Product.all
end
desc "Product By Id", {
:notes => <<-NOTE
Get Product By Id
__________________
NOTE
}
params do
requires :id, type: Integer, desc: "Product id"
end
get ':id' do
begin
product = Product.find(params[:id])
rescue ActiveRecord::RecordNotFound
error!({ status: :not_found }, 404)
end
end
desc "Delete Product By Id", {
:notes => <<-NOTE
Delete Product By Id
__________________
NOTE
}
params do
requires :id, type: Integer, desc: "Product id"
end
delete ':id' do
begin
product = Product.find(params[:id])
{ status: :success } if product.delete
rescue ActiveRecord::RecordNotFound
error!({ status: :error, message: :not_found }, 404)
end
end
desc "Update Product By Id", {
:notes => <<-NOTE
Update Product By Id
__________________
NOTE
}
params do
requires :id, type: Integer, desc: "Product id"
requires :name, type: String, desc: "Product name"
requires :price, type: BigDecimal, desc: "Product price"
optional :old_price, type: BigDecimal, desc: "Product old price"
requires :short_description, type: String, desc: "Product old price"
optional :full_description, type: String, desc: "Product old price"
end
put ':id' do
begin
product = Product.find(params[:id])
if product.update({
name: params[:name],
price: params[:price],
old_price: params[:old_price],
short_description: params[:short_description],
full_description: params[:full_description]
})
{ status: :success }
else
error!({ status: :error, message: product.errors.full_messages.first }) if product.errors.any?
end
rescue ActiveRecord::RecordNotFound
error!({ status: :error, message: :not_found }, 404)
end
end
desc "Create Product", {
:notes => <<-NOTE
Create Product
__________________
NOTE
}
params do
requires :name, type: String, desc: "Product name"
requires :price, type: BigDecimal, desc: "Product price"
optional :old_price, type: BigDecimal, desc: "Product old price"
requires :short_description, type: String, desc: "Product old price"
optional :full_description, type: String, desc: "Product old price"
end
post do
begin
product = Product.create({
name: params[:name],
price: params[:price],
old_price: params[:old_price],
short_description: params[:short_description],
full_description: params[:full_description]
})
if product.save
{ status: :success }
else
error!({ status: :error, message: product.errors.full_messages.first }) if product.errors.any?
end
rescue ActiveRecord::RecordNotFound
error!({ status: :error, message: :not_found }, 404)
end
end
end
end
Now go back to config/routes.rb and add:
mount Products::ProductsAPI => '/api/products'
Run db:migrate from console to run the migrations:
rake db:migrate
You can check the application on our Github page. If you have any question please ask below.



