CSC/ECE 517 Spring 2013/ch1a 1b mh

From Expertiza_Wiki
Jump to navigation Jump to search

Ruby CRUD - Update and Destroy

Introduction

Topic Write-up

SaaS - 3.16 Finishing CRUD


Modern application delivery has undergone drastic changes in recent years, and the introduction of SaaS, or Software-as-a-Service, provides a way for software to be provided "on demand" to a user as needed. Essentially, what this means, is that users interact with a given application through a web based interface referred to as a "thin client", and another computer actually performs the applications computational tasks. The thin client is primarly tasked with displaying the application interface, and relaying the users actions back to the primary computational unit. Such a delivery model has many advantages, such as eliminating the need to install an application locally, and reducing the computational demand on the local client. In addition, this allows the hosting computer to provide an indeterminate number of interfaces to multiple users, as well as being able to free the resources used supplying the service once the user no longer needs them. The architecture used for many SaaS solutions is based on the "Model-View-Controller" architecture. This architecture is modular, easily expandable, very flexible, and many solutions have been created to help software engineers develop SaaS enviornments and by extension the delivery methods needed to produce these products. This article is primarily focused on the development suite, Ruby on Rails, and in particular, 2 types of actions used by this software - Update and Delete.

Rails is a framework application development suite for SaaS development, based on the object-oriented language Ruby, and based on the afore-mentioned Model-View-Controller architecture. Rails favors the axiom of "convention over configuration", and developers are encouraged to learn the conventions of Rails, and let the framework handle the actual implementation, rather than concern them selves with extensive configuration, an approach which greatly increases software development productivity.

Overview of Model-View-Controller Architecture

The Model–View–Controller architecture is a software design pattern which separates the actual representation of data from the user's interaction with it, including both the user's view of the data and the actual input method.[1] The three essential parts of this architecture, as indicated by the name, are Models, Views, and Controllers


Diagram of MVC Architecture Source


Model

A model is concerned with the actual data in it's native format, and dictates how the data is stored and represented in the database. The model is also responsible for how the data gets changed. This dictates that the model contains the code which comunicates with the storage tier/database. Generally speaking, there are multiple models in an MVC application; one for each 'table' schema) in the database. In Rails, a model is based on the ActiveRecord::Base class.

Example of how to set up a Model using Active Record in Rails

class Mymodel < ActiveRecord::Base
end

View

A View contains the code which dictates how the data is represented to the user, and acts as the interface between the user and the data in terms of interaction. It is not unusual for an application to have many different views, and the views in general are small and contain only code concerned with printing the data on screen. This means that views do not contain code to sort the data before it's displayed, as this violates the encapsulated structure of MVC architecture. In Rails, views inherit from the ActionView::Base class.

Example of View code

!!! 5
%html
   %head
      %title Example Title.
      = stylesheet_link_tag 'application'
      = javascript_include_tag 'application'
      = csrf_meta_tags

   %body
      = yield

Controller

Controllers act as the interface and mediator between the user and the data. While a view displays the data, controller code is what actually dictates what happens in response to a users actions. Controller tend to exist in a one-to-one relationship with models, and allow such actions as requesting the model to retrieve or modify data, or prepare a data set for display. In Rails, controllers inherit from the ApplicationController class, which itself inherits from the ActionController::Base class.


Example of how to set up a Controller using an Active Controller in Rails

class Mycontroller < ApplicationController
   def index
      @myobject = Myobject.all
   end
end


Example of a Controller Template Markup supporting the Index RESTful action.

%h2 All Myobjects

%table#myobjects
   %thead
      %tr
         %th Object Name
         %th Quantity
         %th GeneratedDate
   %tbody
     - @myobjects.each do |object|
      %tr
         %td= myobject.name
         %td= myobject.quantity
         %td= myobject.generateddate
         %td= link_to "More about #{myobject.name}". myobject_path(myobject)

This architecture provides several advantages, including separation of functionality (also called separation of concerns), which facilitates easy maintenance, and reusability. Reusability is addressed in one of Rails central axioms: DRY, or Don't Repeat Yourself.

CRUD

Rails development implements a groups of shortcuts for database manipulation referred to as CRUD. CRUD stands for the 4 central actions which can be performed on a database: Create, Read, Update, and Delete,

The MVC framework of Rails is an example of a Representational State Transfer, better known as RESTful, archictecture. In classic REST, the CRUD operations correspond to the HTTP directives PUT (Create), GET (Retrieve), POST (Update) and DELETE (Delete).

CRUD is a general term which describes what anyone would want to do with a database: create records, access them, update them or delete them. Direct database access should not be used when programming with the Rails framework; once the database is set up with the scaffold, all further interaction should be using CRUD. CRUD could be implemented with a filing cabinet. REST specifies a means of implementing CRUD using specified HTTP directives and simple URIs.

REST was first described in this 2002 paper by Roy Fielding.


Update

The Update action in the Rails MVC architecture is actually composed of 2 separate actions: Edit and Update. The first action, Edit, displays the form with editable information. It is preferable to pre-populate the editable fields with data, rather than provide blank fields. This helps prompt the user as to what exactly is needed in a specific field, and also provides a default value that the user may accept without change. The second action, Update, applies the updated information. This commits the data to the database.

The update action uses the HTTP verb POST in conjuction with the URI helpers provided by Rails, where :id is the primary key of the affected data being updated.

The HTTP verb POST can be used either directly by a web browser or in a RESTful application to perform updates.


Example of code needed for a successful Edit/Update action

#locate the object
	@object = Object.find params(:id)

#update it
	@object.update_attributes!(params[:param])

#inform of successful update
	flash[:notice]

#go to the updated object
	redirect_to object_path(@object)

Comparison of Update and Create Actions

The Edit/Update code pair is very similar to the New/Create pair used in the first action of CRUD implementations. For creation, a new object is created using the new methods and the attributes are set to initial values. This object is not stored in the database until the save directive. Alternatively the create method can be used which combines the two operations.

For update, an object is accessed by means of find. The attributes of the object and the database are then updated by calling update.

source

def edit
   @movie = Movie.find params[:id]
end

def update
   @movie = Movie.find params[:id]
   @movie.update_attributes!(params[:movie])
   flash[:notice] = "#{@movie.title} was successfully updated."
   redirect_to movie_path(@movie)
end

Model Level Validation

Model Level Validation should be included in the design. Model-level validations are a means to ensure that erroneous data is not saved in the database. Even more important, they can protect against malicious attacks. By putting the validation in the model, it cannot be bypassed and is not dependent on any particular database.

In some cases, malicious users have used SQL injection attacks. In an SQL injection attack a database entry field contains SQL commands. When passed to the database these commands can cause unwanted effects ranging from denial of service to exposure of confidential information. Validation should ensure that no SQL commands are passed to update_attributes.

Delete

The Delete (Destroy) action performs exactly what it sounds like: it deletes an object from the database (operating on an ActiveRecord model). In a similar fashion to Edit/Update, it is preferable to return the user to some other useful page in the view after a successful Delete action, rather than a simple "Delete successful" message.

The destroy action uses the HTTP verb DELETE in conjuction with the URI helpers provided by Rails, where :id is the primary key of the data to be deleted.

HTTP verb DELETE would be used in RESTful applications. A web browser will not typically send an HTTP DELETE request and instead would use an HTTP POST with a field specifying the delete method.


Overview of code actions needed for a successful Delete action

#locate the object
        @object = Object.find params(:id)

#destroy it
	@object.destroy

#inform of successful destroy
	flash[:notice] = "Object '#{@object.title}' deleted."


It's important to note that there are two separate methods in Rails ActiveRecord: delete and destroy. Both of these methods will delete data from the database, however they do so in different ways.

Delete will delete data from the database without instantiating the objects referenced. Destroy instantiates the objects, allowing various methods inside them to run, including methods which might be used to notify other objects about the destruction of the object. For example we might have methods in an object called before_destroy or after_destroy which will provide guidance on cleaning up other areas if the object is deleted from the database. Those methods can only be called if we use destroy rather than delete to remove the object from the database.

There are cases, however, where delete is useful. Since it does not instantiate the objects it will have faster performance than destroy. If we know for a fact that there is no clean-up to perform and we have a large number of objects to delete it may be advantageous to use delete. There should be a specific reason to use delete, however.

One further subtle distinction is that since destroy instantiates the object before deletion, the object is available in memory after it has been deleted from the database. Thus, as in the example above, it is possible to reference attributes of the deleted object after it is deleted from the database.

The usage of destroy completes the object cycle of new and create. After destruction the object is in a state much like it is after new but before create: the object exists and can be referenced but is not a part of the database.

Pitfalls in MVC Design

Direct Database Manipulation

While it may seem easy (and thus tempting) to directly modify data in the database (such as by issue direct SQL commands), this practice is generally frowned on in Rails development. The reason is less because it interferes with the MVC architecture, and more because the changes are not automatically reproducible. Rails provdes Rake tools which allow easy, reproducible database migrations, and should be entirely responsible for setting up the databases. Rails provides simple tools to automate this task, and allows a developer to save snapshots of database structure which they can roll the database back to when current changes are found to be inappropriate or damaging to the application.

"Fat" controllers and "fat" views trap

While there is a tempatation to put majority of code in controller, controllers should be kept as lean as possible. Generally speaking, code should be added to the Model, although it is inevitable that some computation will occur here - notably sorting or data filtering.

Similarly, Views should be kept as thin is possible. While it may be tempting to place code which conditionally displays (filters) data in the View, the correct location for it is in the Controller.

Ultimately, the Model should contain the majority of the logic needed. This helps to keep the Views and Controllers more encapsulated, as well as improving their reusability - after all, its hard to reuse a view when it has code specific to sorting one particular model's data.

Following these guidelines makes the code easily amenable to the Service-Oriented Architecture; a trivial controller change allows the code to be refactored to respond correctly to XML or JSON.

Migration to Service Oriented Architecture

Another benefit of thin controllers and views is an easy migration to a Service Oriented Architecture (SOA).

SOA is used in Software As a Service (SAAS) applications. Typically in SOA, another computer, rather than a human user, is accessing the web service. Hence a more efficient method of passing the data from machine to machine is employed. This often takes the form of an XML interface for input/output, which allows direct access to the data instead of using “screen scraping” techniques to convert HTML or other human-readable output into computer-readable data.

In the example code below the non-SOA path does a redirect to HTML which would be the normal way to display the results to the user. An SOA application can be defined by a single line of code which specifies an XML rendering if the client prefers XML.

def create
  @movie = Movie.find params[:id]
   @movie.update_attributes!(params[:movie])
   respond_to do |client_wants|
      client_wants.html { redirect_to movie_path(@movie) } # as before
      client_wants.xml { render :xml => @movie.to_xml }
   end
end

XML can be provided natively within Rails. If a client wants JavaScript Object Notation (JSON) this can be accomplished using an add-on JSON converter such as The JSON Implementation For Rails. The usage of JSON would be similar to XML. JSON would be preferred in cases where the data exchanged is desired to be as small as possible such as disk storage or certain network transfers.

Summary

In a Model / View / Controller framework, Rails encourages you to put proportionately more code into models. The model represents the objects which the user will see and manipulate. Once the code is in the model it can be reused in methods and controllers. Controllers and views are intended to be simple. Views and controllers will often follow the paradigm of convention over configuration. Views and controllers ideally will follow typical conventions of Ruby on Rails.

Keep in mind the conventions of rails. They will save you a lot of work and ensure fewer bugs. In general less code means fewer bugs if we assume that the number of bugs per line of code is constant.

Debugging a web application is tricky due to the lack of a console. Debugging is greatly assisted by the judicious use of logging, tracking by puts or printf, and the interactive debugger.


References

1. Engineering Long-Lasting Software: An Agile Approach Using SaaS & Cloud Computing, by Armando Fox and David Patterson: Publisher: Strawberry Canyon LLC, 2012
2. http://www.youtube.com/watch?v=P0g6JtcWm2o
3. Getting Started with Rails http://guides.rubyonrails.org/getting_started.html
4. Ruby on Rails Tutorial by Michael Hartl http://ruby.railstutorial.org/
5. Principled Design of the Modern Web Architecture, Roy T. Fielding and Richard N. Taylor http://www.ics.uci.edu/~taylor/documents/2002-REST-TOIT.pdf
6. Ruby on Rails WikiBook http://en.wikibooks.org/wiki/Ruby_on_Rails
7. Ruby on Rails: Understanding the MVC Architecture http://www.youtube.com/watch?v=3mQjtk2YDkM