CSC/ECE 517 Fall 2012/ch1b 1w38

From Expertiza_Wiki
Revision as of 06:07, 3 October 2012 by Nmsarda (talk | contribs)
Jump to navigation Jump to search

    Using scaffold to create framework

Rails scaffolding is a quick way to generate some of the major pieces of an application. If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.
In Rubymine, we can create a scaffold by right clicking on the current Project->New->Run Rails Generator ->scaffold. This will open a dialog box to create a scaffold where we can enter the name and the attributes along with their data type. Once the scaffold has been created RubyMine will generate the model, view and controller files respectively. These files do not contain any specific methods but they provide a framework which is useful for general applications. The user can modify the MVC behavior according to his application requirements. Each scaffold has create, read, update and delete methods declared in the controller class. A user can modify the behavior of the controller through these methods.

    Controllers for CookBook Application

The Cookbook application starts by entering a URL in the browser which sends the request to the web server. Since our web server supports Rails application, it will hand over the control to a Rails controller. A controller ‘controls’ the entire application by responding to the incoming requests as needed. For example, consider the following URL http://localhost:3000/categories/show which is decoded by the controller to know that its action named show has been invoked. All the controllers inherit only the ApplicationController which in turn inherits the ActionController::Base class. ActionContoller module provides the basic support for the controllers in Rails. In our Cookbook application we generate Categories controller using the scaffold mechanism which populates the controller with the basic methods such as index, create, show, new, edit, update and destroy. A simple example of index method is as follows:

def index
    @categories = Category.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @categories }
    end
  end

In ruby, @ operator is used to declare instance variables. The index method makes use of @categories instance variable which is populated with all the records in the Category table. The respond_to method is used for responding to particular type of request. It contains a block and a variable |format| which determines whether the incoming request should be responded with HTML (when we are interacting with a user) or JSON format (if we are returning an object). After executing the code, the controller will render a template of the same name. (For this example, the template is views/categories/index.html.erb). Similarly the show method is defined as follows:

def show
    @category = Category.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @category }
    end
  end

The show method is used to display a particular object. The show method needs to access database to search for the given object. This is done using the find method which takes params[:id] as argument. The params[:id] contains the id or primary key of the object which is passed as a parameter to look up in the database and the result is stored in the instance variable. The result is displayed by rendering the show.html.erb file.

Active Record
In ruby, a program deals with objects which are mapped into database relational databases. This kind of mapping is facilitated by the Active Record.
Active Record maps the Rails classes in to database tables and rails objects into database records. This makes it easy to perform actions on database tables by invoking the class methods. Another benefit of Active Record is database migration. Migrations can manage the evolution of a schema used by several physical databases. It’s a solution to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to push that change to other developers and to the production server. With migrations, you can describe the transformations in self-contained classes that can be checked into version control systems and executed against another database that might be one, two, or five versions behind.You can find a lot more details about rails migration here[8].

Method Pairs
There are couple of method pairs such as new-create and edit-update which are executed in sequence. In method pair one method is responsible for display preparation and the other method validates the data and attempts it to save in the database.

def new
    @category = Category.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @category }
    end

  end
def create
    @category = Category.new(params[:category])

    respond_to do |format|
      if @category.save
        format.html { redirect_to @category, notice: 'Category was successfully created.' }
        format.json { render json: @category, status: :created, location: @category }
      else
        format.html { render action: "new" }
        format.json { render json: @category.errors, status: :unprocessable_entity }
      end
    end
  end

The new and create methods are known as method pairs which execute in sequence. The new method is used to instantiate a new Category object whereas create method is used to successfully save the object to the database. After execution of the new method, the controller renders the new.html.erb file which displays a form to create a new Category object. The user enters the required information in the form and the object id is returned in the URL on submitting the form. Consider the following URL is generated on submitting the form: http: //localhost:3000/categories/3. This means that Category object with id =3 will be created. The details of the object will be processed by the create method. The controller invokes the create method to store the object in the database. An observation can be made about the @category variable in the create method.

@category = Category.new(params[:category])

The parameters passed in the URL after creating a new object is stored in the params hash (denoted by params[]). In this case the params[] contains an object with category_id = 3. The params[] assigns the data of object with category_id =3 to the instance variable. The data entered in the instance variable is validated using the @category.save method. If no errors are found then the object stored in the database and a message on successful creation of the object is displayed to the user. However, if the data entered in the object contains an error, the user is redirected to the new.html.erb page to resubmit the information and the errors are displayed. Another example of such method pair is the edit and update methods. The edit method displays the table entry for an existing object, while the update method is invoked when the changes have been submitted.

    Models of CookBook Application

Model handles the data processing in a web application. Actions interact with the model to manipulate the data. Rails is capable of handling databases and this is facilitated by the model. You can base models on the Rails ActiveRecord module which contains support databases. In our Cookbook application we have two models namely: Category and Recipe. They are represented by files category.rb and recipe.rb and each file is used to describe the relation between two classes. Let’s take a look at the two models
category.rb

class Category < ActiveRecord::Base
  has_many :recipes
end


recipe.rb

class Recipe < ActiveRecord::Base
  belongs_to :category
  validates :title, :presence => true
  validates :instructions, :presence => true
  validates :category, :presence => true
end


  • Relationships in models
    one-to-one
    class Syllabus < ActiveRecord::Base
    	belongs_to :course¬
    end
    
    class Course < ActiveRecord::Base
    	has_one :syllabus
    end 
    


    many-many
    class Student < ActiveRecord::Base
    	has_and_belongs_to_many :courses
    end
    
    class Course < ActiveRecord::Base
    	has_and_belongs_to_many :students
    end
    


In cookbook, we have many-one relationship between recipes and categories.

    Views of Cookbook application

What is V in MVC? ‘V’ refers to views. Views are the actual interface that users can see. It is through views, that a user may request some data to be viewed or submits some data for processing.
Views in rails are of the form file_name.html.erb. Here erb means embedded ruby. What html.erb denotes is some ruby code is embedded in HTML code. Like HTML, embedded ruby also uses tags to represent data. However, to distinguish it from the HTML code, ruby is embedded between <% %> if some non-assignment ruby operation is to be performed. If something needs to be displayed on screen, <%= %> is used. Views are present in “app/views” folder in the rails application.
Name of the view files is same as the name of their respective controller. For e.g.:- if I have a controller movie with an action new, the corresponding view page will be new.html.erb in the app/views/movie directory.
The cookbook application views are as follows:

Recipe Controller Views
1. index.html.erb

<h1>Listing categories</h1>

<table>
  <tr>
    <th>Name</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @categories.each do |category| %>
  <tr>
    <td><%= category.name %></td>
    <td><%= link_to 'Show', category %></td>
    <td><%= link_to 'Edit', edit_category_path(category) %></td>
    <td><%= link_to 'Destroy', category, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Category', new_category_path %>
  • <% @recipes.each do |recipe| %>

Here @recipes is an instance variable of model recipe defined in index action of the controller ‘recipes’. It contains all the recipes currently present in our application. Then each recipe is taken from this instance variable and is assigned to the local variable recipe. All the subsequent lines display the recipe details (name, description and instruction).

  • <%= link_to ‘Show’, recipe %>

link_to is an ruby operator which is used to redirect the control to some other page. ‘Show’ is displayed as it is on the page and is an hyperlink. It links to a page pointed by recipe. The path is defined in the routes.rb file.

2. _form.html.erb
This is a partial. Partials are devices used for breaking the code in smaller, more manageable chunks. This helps in modularity. With a partial, code for rendering a particular response is moved to its own file. So for e.g. we can use this same partial for creating and editing information, because the information involved is essentially the same.

<%= form_for(@recipe) do |f| %>
  <% if @recipe.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@recipe.errors.count, "error") %> prohibited this recipe from being saved:</h2>

      <ul>
      <% @recipe.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
    <%=  f.label :category %>
    <%=    select('recipe','category_id', Category.all.collect {|c| [c[:name],c[:id]]}) %>
  <div class="field">
    <%= f.label :description %><br />
    <%= f.text_field :description %>
  </div>
  <div class="field">
    <%= f.label :instructions %><br />
    <%= f.text_area :instructions %>
    <%= f.submit %>
  </div>
<% end %>
  • <%= form_for(@recipe) do |f| %>

This statement means to render a form for the instance variable @recipe. If this page is being used for creating a new recipe, then the values for each key in the @recipe hash will be empty. If it is used for editing a recipe, @recipe will contain information corresponding to the recipe being edited.

  • <% if @recipe.errors.any? %>

While saving an recipe or updating a recipe in the database, some violations may occur. Like the recipe name is left empty or its description is empty and so on. So while saving this information, database will signal some error. The errors are sent back to this page and are caught in @recipe.errors.any? statement.

  • <%= select('recipe','category_id', Category.all.collect {|c| [c[:name],c[:id]]}) %>

This statement creates a dropdown box.

    ‘recipe’ : refers to the object to which the selected value is to be assigned. In this case , it is the recipe object
    ‘category_id’ : create a ‘category_id’ key in the recipe hash.
    Category.all.collect {|c| [c[:name],c[:id]]} : recover all categories from the database and list them in the dropdown box. The value for each name is c[:id]. Whenever a category is selected by the user, its corresponding id is assigned to the ‘category_id’ key in the ‘recipe’ object.



3. new.html.erb
This page is used to create a new recipe. This page simply renders the partial described above.

<h1>New recipe</h1>
<%= render 'form' %>
<%= link_to 'Back', recipes_path %>
  • <%= render ‘form’ %>

This statement renders the partial.

4. edit.html.erb This page is used to edit a existing recipe. This page also renders the partial.

<h1>Editing recipe</h1>
<%= render 'form' %>
<%= link_to 'Show', @recipe %> |
<%= link_to 'Back', recipes_path %>

5. show.html.erb This page is used to show a particular recipe requested by the user. The recipe requested is captured by the controller in the @recipe instance variable and is passed to this view.

<p id="notice"><%= notice %></p>
<p>
  <b>Name:</b>
  <%= @recipe.name %>
</p>
<p>
  <b>Description:</b>
  <%= @recipe.description %>
</p>
<p>
  <b>Instructions:</b>
  <%= @recipe.instructions %>
</p>
<%= link_to 'Edit', edit_recipe_path(@recipe) %> |
<%= link_to 'Back', recipes_path %>


The Category Views are very similar to the recipe Views and can be created similarly.