CSC/ECE 517 Fall 2011/ch4 4d ch: Difference between revisions
Line 11: | Line 11: | ||
==Ruby on Rails== | ==Ruby on Rails== | ||
[http://en.wikipedia.org/wiki/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. | [http://en.wikipedia.org/wiki/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. | ||
[[File:RoR.png]] | |||
<b>Metaprogramming</b>: 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. | <b>Metaprogramming</b>: 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. |
Revision as of 15:20, 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
Then the scaffold will automatically create files.
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
The relationship between recipes and categories is one to many.It is represented in the db:migrate files that define the classes recipes and categories.
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
MVC was first proposed by Trygve Reenskaug in 1979. He broke applications into three types of components: models, which is responsible for maintaining the state of the application; views, which are responsible for generating a user interface; and controllers, which orchestrate the applications by receiving events from outside world, interact with model, and display an appropriate view to user. This separation of concerns led to far less computing, and in turn made the code easier to write and maintain.
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
- [1]: http://en.wikipedia.org/wiki/Integrated_development_environment
- [2]: http://en.wikipedia.org/wiki/Ruby_on_Rails
- [3]: http://courses.ncsu.edu/csc517//common/lectures/notes/lec9.pdf
- [4]: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
- [5]: Sam Ruby, Dave Thomas, David Heinemeier Hansson.agile web development with rails (fourth edition)
- [6]: http://en.wikipedia.org/wiki/Don't_repeat_yourself