CSC/ECE 517 Fall 2011/ch4 4d ch: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 227: Line 227:
</pre>
</pre>


=<b>Views of Cookbook application</b>=
=Views of Cookbook application=
 
edit.html.erb
<pre>
<pre>
edit.html.erb
<h1>Editing category</h1>
<h1>Editing category</h1>
<%= render 'form' %>
<%= render 'form' %>
<%= link_to 'Show', @category %> |
<%= link_to 'Show', @category %> |
<%= link_to 'Back', categories_path %>
<%= link_to 'Back', categories_path %>
</pre>
</pre>


The form refers to a partial named _form.html.erb. To stick to the DRY(Don't repeat yourself) principles. With partials, we can move the code for rendering a particular piece of a response to its own file so modularity can be achieved.
The form refers to a partial named _form.html.erb. To stick to the [http://en.wikipedia.org/wiki/Don't_repeat_yourself DRY](Don't repeat yourself) principles. With partials, we can move the code for rendering a particular piece of a response to its own file so modularity can be achieved.


_form.html.erb
<pre>
<pre>
_form.html.erb
<%= form_for(@category) do |f| %>
<%= form_for(@category) do |f| %>
   <% if @category.errors.any? %>
   <% if @category.errors.any? %>
     <div id="error_explanation">
     <div id="error_explanation">
       <h2><%= pluralize(@category.errors.count, "error") %> prohibited this category from being saved:</h2>
       <h2><%= pluralize(@category.errors.count, "error") %> prohibited this category from being saved:</h2>
       <ul>
       <ul>
       <% @category.errors.full_messages.each do |msg| %>
       <% @category.errors.full_messages.each do |msg| %>
         <li><%= msg %></li>
         <li><%= msg %></li>
       <% end %>
       <% end %>
       </ul>
       </ul>
     </div>
     </div>
   <% end %>
   <% end %>


   <div class="field">
   <div class="field">
     <%= f.label :name %><br />
     <%= f.label :name %><br />
     <%= f.text_field :name %>
     <%= f.text_field :name %>
   </div>
   </div>


   <div class="actions">
   <div class="actions">
     <%= f.submit %>
     <%= f.submit %>
   </div>
   </div>
<% end %>
<% end %>


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


   <div class="field">
   <div class="field">
     <%= f.label :title %><br />
     <%= f.label :title %><br />
     <%= f.text_field :title %>
     <%= f.text_field :title %>
   </div>
   </div>


   <div class="field">
   <div class="field">
     <%= f.label :category %><br />
     <%= f.label :category %><br />
     <%= select("recipe", "category_id", Category.find(:all).collect{ |c| [ c.name, c.id] }) %>
     <%= select("recipe", "category_id", Category.find(:all).collect{ |c| [ c.name, c.id] }) %>
   </div>
   </div>


   <div class="field">
   <div class="field">
     <%= f.label :description %><br />
     <%= f.label :description %><br />
     <%= f.text_field :description %>
     <%= f.text_field :description %>
   </div>
   </div>


   <div class="field">
   <div class="field">
     <%= f.label :instructions %><br />
     <%= f.label :instructions %><br />
     <%= f.text_area :instructions %>
     <%= f.text_area :instructions %>
   </div>
   </div>


   <div class="actions">
   <div class="actions">
     <%= f.submit %>
     <%= f.submit %>
   </div>
   </div>
<% end %>
<% end %>
</pre>
</pre>


Line 354: Line 303:


show.html.erb
show.html.erb
<pre>
<pre>
<p>
<p>
   <b>Name:</b>
   <b>Name:</b>
   <%=h @category.name %>
   <%=h @category.name %>
</p>
</p>
<%= link_to 'Edit', edit_category_path(@category) %> |
<%= link_to 'Edit', edit_category_path(@category) %> |
<%= link_to 'Back', categories_path %>
<%= link_to 'Back', categories_path %>
<p id="notice"><%= notice %></p>
<p id="notice"><%= notice %></p>
<p>
<p>
 
<b>Title:</b>
  <b>Title:</b>
 
   <%= @recipe.title %>
   <%= @recipe.title %>
</p>
</p>
<p>
<p>


  <b>Description:</b>
<b>Description:</b>
 
   <%= @recipe.description %>
   <%= @recipe.description %>
</p>
</p>


<p>
<p>
   <b>Instructions:</b>
   <b>Instructions:</b>
   <%= @recipe.instructions %>
   <%= @recipe.instructions %>
</p>
</p>


<%= link_to 'Edit', edit_recipe_path(@recipe) %> |
<%= link_to 'Edit', edit_recipe_path(@recipe) %> |
<%= link_to 'Back', recipes_path %>
<%= link_to 'Back', recipes_path %>
</pre>
</pre>


Line 404: Line 333:


<pre>
<pre>
<h1>New category</h1>
<h1>New category</h1>
<%= render 'form' %>
<%= render 'form' %>
<%= link_to 'Back', categories_path %>
<%= link_to 'Back', categories_path %>
</pre>
</pre>



Revision as of 15:02, 21 October 2011

Lecture 9: CookBook Application

Introduction

Model-view-controller (MVC)

Model-view-controller(MVC) is an architectural pattern used in software engineering.In complex computer applications that present a large amount of data to the user, a developer often wishes to separate data (model) and user interface (view) concerns, so that changes to the user interface will not affect data handling, and that the data can be reorganized without changing the user interface.

The model-view-controller solves this problem by decoupling data access and business logic from data presentation and user interaction, by introducing an intermediate component: the controller.

Ruby on Rails

Ruby on Rails is an MVC framework. Rails enforces a structure for application, it helps to knits the models, views, and controllers as separate chunks of functionality while the program executes. No more external configuration needed. Rails is packed with features that make you more productive, with many of the following features building on one other.

Metaprogramming: Other frameworks use extensive code generation from scratch. Metaprogramming techniques use programs to write programs. Ruby is one of the best languages for metaprogramming, and Rails uses this capability well. Rails also uses code generation but relies much more on metaprogramming for the heavy lifting.

Active Record: Rails introduces the Active Record framework, which saves objects to the database. The Rails version of Active Record discovers the columns in a database schema and automatically attaches them to your domain objects using metaprogramming.

Convention over configuration: Most web development frameworks for .NET or Java force you to write pages of configuration code. If you follow suggested naming conventions, Rails doesn't need much configuration.

Scaffolding: You often create temporary code in the early stages of development to help get an application up quickly and see how major components work together. Rails automatically creates much of the scaffolding you'll need.

Built-in testing: Rails creates simple automated tests you can then extend. Rails also provides supporting code called harnesses and fixtures that make test cases easier to write and run. Ruby can then execute all your automated tests with the rake utility.

Three environments: Rails gives you three default environments: development, testing, and production. Each behaves slightly differently, making your entire software development cycle easier. For example, Rails creates a fresh copy of the Test database for each test run.

Cookbook application

Cookbook application is a basic web application which demonstrates the fundamental way of developing web applications with Ruby on Rails. It is a good example for new developers. Lecture 9 focus on the controllers, models and views of Cookbook application. In this page chapter 2 will first introduce the steps of creating Cookbook application briefly, then part 3, 4 and 5 will analyse controllers, models and views of this application, introduce some important concepts at the same time. You can get an overview of Ruby on Rails application after reading this page.

A simple application to start -- Cookbook application

Start a new project by rails

When we start a ruby on rails application, the rails automatically help us create a brunch of files and folders as follows:

app/ config.ru doc/ lib/ public/ README test/ vendor/
config/ db/ Gemfile log/ Rakefile script/ tmp/

We will mainly use the app/ folder, it contains all the MVC components our project will need. Public/ folder will include some viewers for general purpose. For example the 404 error page, 500 error page, and the "Welcome Aboard" page you will see as an empty project when you open the website on your local machine: http://localhost:3000.

The test folder contains unit test, model test and encapsulate test for automatic generated MVCs, you will also need to create your own test file in this folder to test your code. Rakefile and Gemfile contains many configuration about the environment your Ruby on Rails are running, you will need to change this files to make your project run on certain environment or when you need to deploy your work on Heroku. db/ folder contains migrate file. In config folder, you will need to config the route.rb to set your mainpage, and make changes to certain evens actions.

So much brief introduction for the structure of the files, we will talk about files' functionality in details in following chapter.

In Rubymine, we use Generate -> scaffold to create a MVC structure. Like, we want to create a Categories MVC, a category can be created, edit, display and destroy. We want the categroy to have a name. So we give scaffold:

Categories  name:string 
<pre/>

Then the scaffold will automatically create files.

<pre>
invoke active_record
create db/migrate/20111018000001_create_categories.rb
create app/models/ category.rb
invoke test_unit
create test/unit/ category _test.rb
create test/fixtures/ categories.yml
route resources : categories
invoke scaffold_controller
create app/controllers/ categories_controller.rb
invoke erb
create app/views/ categories
create app/views/ categories /index.html.erb
create app/views/ categories /edit.html.erb
create app/views/ categories /show.html.erb
create app/views/ categories /new.html.erb
create app/views/ categories /_form.html.erb
invoke test_unit
create test/functional/ categories _controller_test.rb
invoke helper
create app/helpers/ categories _helper.rb
invoke test_unit
create test/unit/helpers/ categories _helper_test.rb
invoke stylesheets
create public/stylesheets/scaffold.css

Then we apply rake: db: migrate to migrate data to database. Till now we have created a simple test MVC structure category with the name. Categories can be created as an object, each category object has a name. This name is correspond to the item in database.

Use the same procedure we create the recipe.

Recipe  Title:string  Description:string Instructions:text

And too, we use migrate for Recipe.

Controllers of Cookbook application

Now we took a detailed look at categories_controller.rb. It controls how user actions are interpreted, act as the input mechanism for the views and models

Class CategoriesController< ApplicationController

Obviously, the CategoriesController inherits from ApplicationController, so we want to take a look at ApplicationController, which is also in the Controller folder:

class ApplicationController < ActionController::Base
  protect_from_forgery
end

So we can see that only one sentence is included in ApplicationController, the main function that CategoriesController inherits is from the ActionController::Base. The ApplicationController offers a convenient way to modify some properties for all Controllers without change the Base file.

Inside the CategoriesController defined all methods that would be used, they are: index, show, new, edit, create, update, and destroy. We want to first take a look at the index method:

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

In ruby, @ means create an instance inside method, Category.all means return all the categories, and give the array to @categories instance variable. The definition of respond_to method can be found in mime_responds.rb. It follows with a block with a variable |format|, this segment of code determines whether to respond with HTML (as when we are interacting with a user) or JSON format (if we are returning an object). The response format is determined by HTTP Accept header sent by the client.

After we executing this method, the controller will render a template of that method, this template is in the views folder. (For this example, the template is views/categories/index/html.erb)

Since we have known the function of respond_to method and its block, we'll take a look at what's different betweent different methods. Now we take a look at show method, we have:

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

We mentioned that the find method has been applied to the category object with a parameter of : id. The find method does a database access. The parameter : all (as we have seen in the last method index) told it to retrieve all records from the category table and assign the collection to a variable called @categories, while in the show method, the params is an object that holds all of the parameters passed in a browser request, params[: id] holds the id, or primary key of the object. The current object id is passed through controller as a parameter, and the show will display that specify category with id passed.

From the two methods we mentioned above we can see that Ruby on Rauls deal with objects, and it map objects into relational databases with Active Record. In Active Record, database tables correspond to Rails classes, and database records(rows) correspond to Rails objects.

Method pairs

In the class the professor has mentioned a coulple of method pairs. We have new and create, edit and update. In these kind of method pairs, one method is responsible for the display preparation; and the other method processes the data that was entered and attempts to save it to the database.

For example, the new set a table entry and display it in a window, when the object is created in the database, the create is invoked, create rendering pages and redirect users to other pages.

  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

Similarly, for edit and update method pairs. Edit retrieves a table entry and displays it in a window, while the changes are submitted, update is invoked.

Models of Cookbook application

There are only two files in the model, one each for the tables in the application. Let's take a look at them.

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

A relationship may be one-to-one (e.g. a course has a syllabus and a syllabus belongs to one course)

class Syllabus < ActiveRecord::Base
	belongs_to :course­
end

class Course < ActiveRecord::Base
	has_one :syllabus
end 

one-to-many (e.g. a course has many assignments)


class Assignment < ActiveRecord::Base
	belongs_to :course
end

class Course < ActiveRecord::Base
	has_many :assignments
end

many-to-many (e.g., a course has many students; students have many courses)

class Student < ActiveRecord::Base
	has_and_belongs_to_many :courses
end

class Course < ActiveRecord::Base
	has_and_belongs_to_many :students
end

Views of Cookbook application

edit.html.erb

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

The form refers to a partial named _form.html.erb. To stick to the DRY(Don't repeat yourself) principles. With partials, we can move the code for rendering a particular piece of a response to its own file so modularity can be achieved.

_form.html.erb

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

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<%= 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 :title %><br />
    <%= f.text_field :title %>
  </div>

  <div class="field">
    <%= f.label :category %><br />
    <%= select("recipe", "category_id", Category.find(:all).collect{ |c| [ c.name, c.id] }) %>
  </div>

  <div class="field">
    <%= f.label :description %><br />
    <%= f.text_field :description %>
  </div>

  <div class="field">
    <%= f.label :instructions %><br />
    <%= f.text_area :instructions %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

The _form.html.erb for recipes handles more data from response of recipes.url than categories hence its bigger than that for categories.

show.html.erb

<p>
  <b>Name:</b>
  <%=h @category.name %>
</p>
<%= link_to 'Edit', edit_category_path(@category) %> |
<%= link_to 'Back', categories_path %>
<p id="notice"><%= notice %></p>
<p>
 <b>Title:</b>
  <%= @recipe.title %>
</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 %>

new.html.erb

<h1>New category</h1>
<%= render 'form' %>
<%= link_to 'Back', categories_path %>

Category controller will be invoked when the form is submitted.

Conclusion

Use Ruby on Rails can implement MVC to build various types of web applications. The Cookbook is an easy starter. Readers can learn the basics of Ruby on Rails from this page.


References