Thursday 8 April 2010

Rails - Day 17 - Update and Delete

Rails  - Day 1 - The RubyGem Love Story
Rails -  Day 10 - Testing Rails Controllers
Rails -  Day 13 -ActiveRecord Relationships Part II
Rails - Day 15 - Rails Forms Part II

In the last post, we covered off validation.

We have been using a mock application of an expense tracker to illustrate the key points.  The expense records the expenses incurred running a small business.

In this post we want to finish the application off by completing our pseudo application by allowing updates and deletes of existing records.

Below is the index page of our expenses application that will allow a user to edit and delete existing expenses that we can create with our code thus far:


The edit action is analogous to the new action in that it just transfers control to a web page that will allow us to alter the details of an existing Expense model object.

As we mentioned in this post, the edit url takes the form of /expenses/123/edit.

Below is the haml created for each row in the above table:



This generates the following url:


At the moment, the haml that renders the screen below is all contained in the new.html.haml:



We want to abstract the Expense model rendering from the new.html.haml in order to reuse it from the edit.html.haml which is the template the rails runtime will look for by convention whenever a url similar to the one below is called:

/expenses/124/edit

The rails runtime will predictably look for a template named edit.html.haml by convention.

We extract the formbuilder haml and place it in a partial named _form.html.haml and update the new.html.haml to render from the partial:

The :locals => {:f => f} expression passes the formbuilder instance to the partial.

We can now easily create an edit.html.haml that will incorporate the partial:


Next we want to create an edit action in our ExpenseController that will retrieve the @expense instance from the database:


The @expense_types instance variable will contain a collection of all the ExpenseType objects that are mapped from records in the expense_types table.

With these sparse lines of code in place, we can now display an expense model that is ready for editing when we click on the edit link of one of the expense records that are displayed on the index page.

Rails routes several restful actions onto every resource that is generated by adding an entry similar to, map.resources :expenses to the routes.rb file.

The update action is the correct and obvious choice for updating our model.

The intricacies of setting up the relationships between the objects was done in the last post when we used accepts_nested_attributes_for to enable the complex Expense object to be created from the form fields submitted from the above page.

Below is the simple update action in our ExpenseController that will take care of the update or redirect to the edit and



In the above action, we are loading the expense object into memory and then calling the update_attributes method which updates all the attributes of a model from the passed in hash.  If the object is invalid, the saving fill fail and false will be returned.

In the event of a successful call to update_attributes, the user will be redirected to the index page where a confirmation message will be displayed which has been placed in the flash provider.


Deleting a Model

This is probably the simplest action to complete.  The delete link takes the form of the bin icon in the screenshot below.


This link is rendered from the following HAML:

=link_to "",  {:action => "destroy", :id => e.id}, :method => :delete, :class => "icon-delete", :confirm => "Are you sure"

This will render an anchor tag that links to a controller action called destroy and passing in the id of the expense we want to delete.  You can also see that the javascript confirm mechanism can be hooked up with the :confirm => "Are you sure" expression.

Below is the HTML that is rendered from the HAML:

<a href="/expenses/17" class="icon-delete" onclick="if (confirm('Are you sure')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input'); s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'rW75EzBpl9r103D8zdIzX18upwh1Vs5dyp9RWYhJ31E='); f.appendChild(s);f.submit(); };return false;"></a>

The above link generates a lot of javascript which constructs a form and posts it to the server that will in turn be routed to the destroy action of the ExpensesController

This will call the following action in the ExpensesController:

 We simply retrieve the expense instance and call the destroy method.  We then place the confirmation message in the flash provider and redirect to the index page:

This ends my introduction to rails series. 

You can download the code that I created writing these posts from github here.







No comments:

Post a Comment