27 Oct
Posted by ProCOM
on October 27, 2007 – 12:37 pm - 1,746 views
Pavi has produced an article for Oracle DBAs interested in finding ways of partitioning the tables as well showing a new form of partitioning using Oracle 9i.Business applications are growing at a faster rate than salaries, and so is the data supporting them, especially e-business applications where the data growth has been around 20-30 percent or more annually. Oracle came up with the idea of Partitioning the Tables. Yes ! The name sounds right “Partitioning”, the literal meaning of Partitioning is “The Act of dividing something into parts” and in our case that ‘something’ is nothing but…?
You guessed it right! Data.
Partitioning in Oracle
Partitioning enables tables and indexes or index-organized tables to be subdivided into smaller manageable pieces and these each small piece is called a “partition”. From an “Application Development” perspective, there is no difference between a partitioned and a non-partitioned table. The application need not be modified to access a partitioned table if that application was initially written on a non partitioned tables.
So now you know partitioning in oracle now the only thing that yo u need to know is little bit of syntax and that’s it, and you are a partitioning guru.
Oracle introduced partitioning with 8.0. With this version only, ” Range Partitioning” was supported. I will come to details later about what that means. Then with Oracle 8i ” Hash and Composite Partitioning” was also introduced and with 9i ” List Partitioning”, it was introduced with lots of other features with each upgrade. Each method of partitioning has its own advantages and disadvantages and the decision which one to use will depend on the data and type of application. Also one can MODIFY , RENAME, MOVE, ADD, DROP, TRUNCATE, SPLIT partitions. We will go thru the details now.
Advantages of using Partition’s in Table
1. Smaller and more manageable pieces of data ( Partitions )
2. Reduced recovery time
3. Failure impact is less
4. import / export can be done at the ” Partition Level”.
5. Faster access of data
6. Partitions work independent of the other partitions.
7. Very easy to use
Types of Partitioning Methods
1. RANGE Partitioning
This type of partitioning creates partitions based on the ” Range of Column” values. Each partition is defined by a ” Partition Bound” (non inclusive ) that basically limits the scope of partition. Most commonly used values for ” Range Partition” is the Date field in a table. Lets say we have a table SAMPLE_ORDERS and it has a field ORDER_DATE. Also, lets say we have 5 years of history in this table. Then, we can create partitions by date for, lets say, every quarter. So Every Quarter Data becomes a partition in the SAMPLE_ORDER table. The first partition will be the one with the lowest bound and the last one will be the Partition with the highest bound. So if we have a query that want to look at the Data of first quarter of 1999 then instead of going through the complete data it will directly go to the Partition of first quarter 1999.
This is example of the syntax needed for creating a RANGE PARTITION.
CREATE TABLE SAMPLE_ORDERS
(ORDER_NUMBER NUMBER,
ORDER_DATE DATE,
CUST_NUM NUMBER,
TOTAL_PRICE NUMBER,
TOTAL_TAX NUMBER,
TOTAL_SHIPPING NUMBER)
PARTITION BY RANGE(ORDER_DATE)
(
PARTITION SO99Q1 VALUES LESS THAN TO_DATE(‘01-APR-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q2 VALUES LESS THAN TO_DATE(‘01-JUL-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q3 VALUES LESS THAN TO_DATE(‘01-OCT-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q1 VALUES LESS THAN TO_DATE(‘01-APR-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q2 VALUES LESS THAN TO_DATE(‘01-JUL-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q3 VALUES LESS THAN TO_DATE(‘01-OCT-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2001’, ‘DD-MON-YYYY’)
)
;
the above example basically created 8 partitions on the SAMPLE_ORDERS Table all these partitions correspond to one quarter. Partition SO99Q1 will contain the orders for only first quarter of 1999.
2. HASH Partitioning
Under this type of partitioning the records in a table, are partitions based of a Hash value found in the value of the column, that is used for partitioning. ” Hash Partitioning” does not have any logical meaning to the partitions as do the range partitioning. Lets take one example.
CREATE TABLE SAMPLE_ORDERS
(ORDER_NUMBER NUMBER,
ORDER_DATE DATE,
CUST_NUM NUMBER,
TOTAL_PRICE NUMBER,
TOTAL_TAX NUMBER,
TOTAL_SHIPPING NUMBER,
ORDER_ZIP_CODE)
PARTITION BY HASH (ORDER_ZIP_CODE)
(PARTITION P1_ZIP TABLESPACE TS01,
PARTITION P2_ZIP TABLESPACE TS02,
PARTITION P3_ZIP TABLESPACE TS03,
PARTITION P4_ZIP TABLESPACE TS04)
ENABLE ROW MOVEMENT;
The above example creates four hash partitions based on the zip codes from where the orders were placed.
3. List Partitioning ( Only with 9i)
Under this type of partitioning the records in a table are partitioned based on the List of values for a table with say communities column as a defining key the partitions can be made based on that say in a table we have communities like ‘Government’ , ‘Asian’ , ‘Employees’ , ‘American’, ‘European’ then a List Partition can be created for individual or a group of communities lets say ‘American-partition’ will have all the records having the community as ‘American’
Lets take one example. In fact, we will modify the same example.
CREATE TABLE SAMPLE_ORDERS
(ORDER_NUMBER NUMBER,
ORDER_DATE DATE,
CUST_NUM NUMBER,
TOTAL_PRICE NUMBER,
TOTAL_TAX NUMBER,
TOTAL_SHIPPING NUMBER,
SHIP_TO_ZIP_CODE,
SHIP_TO_STATE)
PARTITION BY LIST (SHIP_TO_STATE)
(PARTITION SHIP_TO_ARIZONA VALUES (‘AZ’) TABLESPACE TS01,
PARTITION SHIP_TO_CALIFORNIA VALUES (‘CA’) TABLESPACE TS02,
PARTITION SHIP_TO_ILLINOIS VALUES (‘IL’) TABLESPACE TS03,
PARTITION SHIP_TO_MASACHUSETTES VALUES (‘MA’) TABLESPACE TS04,
PARTITION SHIP_TO_MICHIGAN VALUES (‘MI’) TABLESPACE TS05)
ENABLE ROW MOVEMENT;
The above example creates List partition based on the SHIP_TO_STATE each partition allocated to different table spaces.
4. Composite Range-Hash Partitioning
This is basically a combination of range and hash partitions. So basically, the first step is that the data is divided using the range partition and then each range partitioned data is further subdivided into a hash partition using hash key values. All sub partitions, together, represent a logical subset of the data.
Lets modify the above example again:
CREATE TABLE SAMPLE_ORDERS
(ORDER_NUMBER NUMBER,
ORDER_DATE DATE,
CUST_NUM NUMBER,
CUST_NAME VARCAHR2,
TOTAL_PRICE NUMBER,
TOTAL_TAX NUMBER,
TOTAL_SHIPPING NUMBER,
SHIP_TO_ZIP_CODE,
SHIP_TO_STATE)
TABLESPACE USERS
PARTITION BY RANGE (ORDER_DATE)
SUBPARTITION BY HASH(CUST_NAME)
SUBPARTITION TEMPLATE(
(SUBPARTITION SHIP_TO_ARIZONA VALUES (‘AZ’) TABLESPACE TS01,
SUBPARTITION SHIP_TO_CALIFORNIA VALUES (‘CA’) TABLESPACE TS02,
SUBPARTITION SHIP_TO_ILLINOIS VALUES (‘IL’) TABLESPACE TS03,
SUBPARTITION SHIP_TO_NORTHEAST VALUES (‘MA’, ‘NY’, ‘NJ’) TABLESPACE TS04,
SUBPARTITION SHIP_TO_MICHIGAN VALUES (‘MI’) TABLESPACE TS05)
(
PARTITION SO99Q1 VALUES LESS THAN TO_DATE(‘01-APR-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q2 VALUES LESS THAN TO_DATE(‘01-JUL-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q3 VALUES LESS THAN TO_DATE(‘01-OCT-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q1 VALUES LESS THAN TO_DATE(‘01-APR-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q2 VALUES LESS THAN TO_DATE(‘01-JUL-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q3 VALUES LESS THAN TO_DATE(‘01-OCT-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2001’, ‘DD-MON-YYYY’)
)
ENABLE ROW MOVEMENT;
The above example shows that each range partition has been further sub-partitioned into smaller partitions based on the list value specified. SHIP_TO_ARIZONA is a sub-partition by a List value AZ. This partition will be present in the main partitions by range SO99Q1 etc.
5. Composite Range-List Partitioning ( Only with 9i)
This is also a combination of Range and List Partitions, basically first the data is divided using the Range partition and then each Range partitioned data is further subdivided into List partitions using List key values. Each sub partitions individually represents logical subset of the data not like composite Range-Hash Partition.
Index organized tables can be partitioned using Range or Hash Partitions
Lets modify the above partition once more.
CREATE TABLE SAMPLE_ORDERS
(ORDER_NUMBER NUMBER,
ORDER_DATE DATE,
CUST_NUM NUMBER,
CUST_NAME VARCAHR2,
TOTAL_PRICE NUMBER,
TOTAL_TAX NUMBER,
TOTAL_SHIPPING NUMBER,
SHIP_TO_ZIP_CODE,
SHIP_TO_STATE)
TABLESPACE USERS
PARTITION BY RANGE (ORDER_DATE)
SUBPARTITION BY LIST(SHIP_TO_STATE)
SUBPARTITION TEMPLATE(
SUBPARTITION SP1 TABLESPACE TS01,
SUBPARTITION SP2 TABLESPACE TS02,
SUBPARTITION SP3 TABLESPACE TS03,
SUBPARTITION SP4 TABLESPACE TS04,
SUBPARTITION SP5 TABLESPACE TS05)
(
PARTITION SO99Q1 VALUES LESS THAN TO_DATE(‘01-APR-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q2 VALUES LESS THAN TO_DATE(‘01-JUL-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q3 VALUES LESS THAN TO_DATE(‘01-OCT-1999’, ‘DD-MON-YYYY’),
PARTITION SO99Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q1 VALUES LESS THAN TO_DATE(‘01-APR-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q2 VALUES LESS THAN TO_DATE(‘01-JUL-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q3 VALUES LESS THAN TO_DATE(‘01-OCT-2000’, ‘DD-MON-YYYY’),
PARTITION SO00Q4 VALUES LESS THAN TO_DATE(‘01-JAN-2001’, ‘DD-MON-YYYY’)
)
ENABLE ROW MOVEMENT;
With Oracle 9i, there is also a feature to create indexes on the partitions. The indexes can be:
a. Local indexes
This is created the same manner as the index on existing partitioned table. Each partition of a local index corresponds to one partition only.
b. Global Partitioned Indexes
This can be created on a partitioned or a non-partitioned tables. But for now, they can be partitioned using the ” Range Partitioning” only. For example, in above example, where I divided the table into partitions representing a quarter, a ” Global Index” can be created by using a different ” Partitioning Key” and can have different number of partitions.
c. Global Non- Partitioned Indexes
This is no different than the ordinary index created on a non-partitioned table. The index structure is not partitioned.
Conclusion
Partitioning greatly enhances the performance, manageability and availability of most databases. Partitioning can be applied to any databases and software shops using such a great option, have greatly improved the user satisfaction for there business applications.
I hope this overview of partitioning will provide some answers to What? When? Who? Where ? Why and How? of partitioning in Oracle.
For detailed syntax and documentation refer to Oracle documentation. This article does not claim to provide a full understanding or the partitioning feature in oracle. Oracle has been modifying this feature with new releases. So lot more to come. Watch out.
—
by Pavi Agrawal
For those who use databases but are not familar to the Oracle 9i database, perhaps you should read about the datatypes used in Oracle 9i and how they can be used to support object orientation.It is assumed that you have read my previous Oracle article based on creating an Oracle database in the latest Oracle database 9i. Before we can create a table, one should sit down and take the time to get to know the datatypes available for Oracle.
Also, upon reading articles relating to the Oracle database, you should have come across the term Abstract datatypes. This article will discuss abstract datatypes in depth so that creating a table, which is designed to include abstract datatypes, will inevitably become more understandable. These abstract datatypes, which I like to call user-defined types, are datatypes that behave like objects.
Oracle Datatypes
These Oracle datatypes are as follows:
Character Strings
Note: The VARCHAR2 datatype is the successor of VARCHAR. So it is recommended that you use VARCHAR2 as a variable-sized array of characters.
Number
Date and Time
Intervals
Binaries
Rows
Each ROWID is:
Alternatives for ANSI Standard Datatypes
Instead of using ANSI standard datatypes, you can use Oracle defined datatypes. View the table below to see the Oracle datatype alternative for ANSI standard datatypes.
| ANSI Standard | Oracle Datatype |
| CHARACTER and CHAR | CHAR |
| CHARACTER VARYING and CHAR VARYING | VARCHAR2 |
| NUMERIC, DECIMAL, DEC, INTEGER, INT and SMALLINT | NUMBER |
| FLOAT, REAL, DOUBLE PRECISION | FLOAT |
Abstract Datatypes
In Oracle, one may create there own datatypes. Abstract datatypes allow Oracle to hold a range of datatypes. So, an abstract datatypes can have many parts to it. To do this one needs to create the datatype as an object. This object is made up of one or more datatypes.
Example of an Abstract Datatype
Let’s say that we want a datatype to split up a person’s address. The abstract datatype may be,
CREATE OR REPLACE TYPE persons_address AS OBJECT
(
v_streetNumber NUMBER,
v_streetName VARCHAR2(30),
v_citySuburb VARCHAR2(30),
v_state VARCHAR2(4),
v_postCode NUMBER
);
When we create a table that references this abstract datatype the values must be inserted as
pe
rsons_address(21, ‘Kings Street’, ‘Junkville’, TN, 12345)
You should now know what datatypes exist in Oracle 9i. You should also understand how one could use this datatypes to create abstract datatypes and hence support object orientation.
Now you understand how datatypes work in Oracle, you should be ready to create Oracle tables.
—
by Ben Shepherd
26 Oct
Posted by ProCOM
on October 26, 2007 – 9:32 pm - 1,453 views
Every wondered how the Oracle database is structured? To learn more about the structure and how one would create an Oracle 9i database using the latest features read this article.When creating a database, the main concept is to know how the database is structured in Oracle 9i. You should be aware that ever since the release of Oracle 8i, the Oracle database support object orientated structures. This means that Oracle supports abstract data types and methods.
If you are new to Oracle, then perhaps you are unclear on how data is stored and what files are included internally and externally. With this article you will be clear about how to create a database using Oracle 9i’s latest features.
How Data is Stored
Data in Oracle is stored in tables and accessed data via a relational model. This means that one may use the tables of data items inside the database in a manner such that the tables relate to one another with the use of primary and foreign keys.
Tables that contain a foreign key are often translated as being a lookup table. Each table should have a primary key, so that each row can be uniquely identified. The primary key is used to connect with the foreign key in another table to form a relationship.
Oracle also supports object-orientated structures. This allows the database to include abstract data types and methods. Due to this object orientation property, objects may related to other objects and that object may exist inside other objects.
Files are used to store data in Oracle. Oracle 9i removes the risk of having orphan file. That is, like the name suggests the file doesn’t have a parent, which means that the datafile was not removed when it’s tablespace is removed. To prevent this from ever happening to your database again, Oracle has introduced “Oracle Managed Files”.
Oracle Managed files, abbreviated as OMF, uses a brand new system of storing files. OMF uses file system directories as opposed to filenames for files that are associated with the tablespace.
This means that when a tablespace is created, the files are given a unique system-generated name, using the locations that have been assigned to them along with two new initialization parameters. So when the tablespace is removed, the files will be removed with it.
Another new feature in Oracle 9i is the System Managed Undo, abbreviated as SMO. The SMO was created to make the creation of the database simpler for the DBA and to reduce the problem relating to misused rollback segments. The DBA now creates a tablespace as an UNDO type to allow the Oracle database to dynamically and automatically manage the number of undoes, along with the size of the undo segments within the tablespace.
A database is partitioned using tablespaces. The tablespace named SYSTEM is the databases default tablespace. These tablespaces allows for the grouping of elements within the system that transact with the database.
A tablespace contains files. These files are called datafiles, which are used to physically store data for the database. Notice this property suggests that datafiles are internal and external structure types, since they are parented by a tablespace (internal property) and are physical storage files (external). The datafile may be resized after it has been created.
Rules about Tablespaces and Datafiles
Now that we know how data is stored using Oracle, there are other special physical files that are associated with data storage and will assist in data recovery. The files that are created when the database in created and they are as follows,
According to the Oracle 9i DBA Handbook, a database should consist of 3 or more online redo files. To archive old online redo files before they are inevitably over written, you may set the database to run in ARCHIVELOG mode. This allows the DBA to look further back into the history of transactions with the database.
The other files associated with an Oracle 9i database are called parameter files. There are two types of parameter files. These are,
To create the SPFILE we do the following,
CREATE SPFILE = ‘<directory>\spfile{SID}.ora’
FROM PFILE = ‘init{SID}.ora’
There are two types of parameter that are located in these parameter files. These are,
Now we know how the parameter files work. Well, to access data inside the database, Oracle uses a set of background processes that are shared between users. The DBA Handbook states that an instance is “ a set of memory structures and background processes that access a set of database files.” Every instance has a large memory structure known as the System Global Area, abbreviated as SGA. The SGA will be explained in another article.
But where do the parameter fit in?
Well the parameters are used to set the size and composition of an instance and are the stored in one of the two specific parameter files as stated previously. The parameter file is read during the startup process. So, to access a database one must start an instance, which may call either the PFILE or the SPFILE. If there exist an SPFILE, then the STARTUP command will read that parameter file. If the SPFILE doesn’t exist, the default SPFILE is read. If that doesn’t exist than the PFILE is read. If you don’t wish for the server to read the PFILE, then simply override the default SPFILE with the PFILE.
STARTUP PFILE = ‘<directory>.init{SID}.ora’
Now you have a good understanding of architecture of an Oracle database, let us create one.
Create a Database
There are two ways that you can create a database in Oracle 9i. One way is to use the Oracle Database Configuration Assistant. Using the Oracle Database Configuration Assistant makes sure that the new database is optimized with Oracle 9i’s latest features. Simply read the instruction and follow the prompts. The other way to create a database is to do it manually. This will now be demonstrated.
Create a suitable directory structure for the new database. Once this is done, copy the init.ora file into a new file named init{SID}.ora into new directory.
Now you have created a parameter file, you’ll need to declare an Oracle SID name. Keep the SID consistent with the parameter filename. The command to do this action will depend on the platform you have the DBMS installed. If you are using Windows, simply type,
Set ORACLE_SID = {SID}
Alternatively, on the a unix platform, the DBA must type,
Export ORACLE_SID = {SID}
Use SQL*Plus under / as sysdba and set the database to nomount mode.
So, if we wish to override the default spfile to start the new instance in nomount mode then type,
Startup nomount pfile = ‘<directory>\init{SID}.ora’
Now we are about to create the database. When creating a new database, write the code into an sql file so that your have a copy of the database features as well as the ability to fix any mistakes in the create database command. Here is the code,
CREATE DATABASE ShepIT
Maxinstances 1
Maxloghistory 1
Maxlogfiles 10
Maxlogmembers 15
Maxdatafiles 100
Datafile ‘<directory>\datafile_01.dbf’
size 300M reuse autoextend on next 15M maxsize unlimited
character set WE8MSWIN1252
national character set AL16UFT16
logfile
group 1 (‘<directory>\redo_01.log’) size 75M
group 2 (‘<directory>\redo_02.log’) size 75M
group 3 (‘<directory>\redo_03.log’) size 75M
default temporary tablespace TEMP
tempfile ‘<directory>\temp_01.dbf’
extent management local uniform size 1M
undo tablespace UNDO_TS datafile ‘<directory>\datafile_01.dbf’
size 125M reuse autoextend on next 15M maxsize unlimited;
Oracle, should respond by saying that the database is created. Once this is done you are ready to load scripts that are needed to support your Oracle products by typing the following commands
@<directory>\catalog.sql
@<directory>\catproc.sql
@<directory>\catexp.sql
Ok, the database is configured. Change the SYSTEM and SYS passwords, to ensure better security for your database.
—
by Ben Shepherd
21 Oct
Posted by ProCOM
on October 21, 2007 – 1:34 pm - 1,396 views
In this conclusion to a two-part article, you’ll learn how to drag and drop slides in our example slideshow to modify it, and how to filter by category. This article is excerpted from chapter six of the book Ruby on Rails: Up and Running, written by Bruce A. Tate and Curt Hibbs (O’Reilly, 2006; ISBN: 0596101325). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
Drag and Drop Everything (Almost Everything)
We have already displayed a list of thumbnails of all photos that are in the slideshow and enabled the user to drag them around to rearrange their order in the slideshow. Now let’s add a second list of thumbnails, showing all photos that are not being used in the slideshow.
We’ll let the user add a photo to the slideshow by dragging it from the list of unused photos and dropping it onto the slideshow thumbnails. Similarly, we can enable the user to remove photos from the slideshow by dragging its thumbnail from the slideshow and dropping on the unused photos list. Finally, we’ll allow the user to filter the unused photos list by category.
As you might expect, we can accomplish all that in a very small amount of code. We will add a mere 58 lines of Ruby code to the models and controllers, 47 lines to the view templates, and 16 lines to our CSS stylesheet! Figure 6-4 gives you a preview of how this is going to look when we’re done.
Figure 6-4. Preview of drag-and-drop slideshow editing
Let’s start by updating the slideshow’s edit template. Edit photos/app/views/slideshows/edit.rhtml to look like this:
<h1>Editing slideshow</h1>
<div id=’slideshow-contents’>
<p style=’text-align: center;’><b>Slideshow Photos</b></p>
<div id=’slideshow-thumbs’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
</div>
<div id=’slideshow-photo-picker’>
<p style=’text-align: center;’><b>Unused Photos</b></p>
<div id=’slideshow-photos’>
<%= render :partial => ‘photo_picker’ %>
</div>
</div>
<div id=’slideshow-attributes’>
<p><%= link_to ‘Play this Slideshow’, :action => ’show’, :id => @slideshow %></p>
<div style=’border: thin solid; padding-left: 1em;’>
<p style=’text-align: center;’><b>Attributes</b></p>
<%= start_form_tag :action => ‘update’, :id => @slideshow %>
<%= render :partial => ‘form’ %>
<%= submit_tag ‘Save Attributes’ %>
<%= end_form_tag %>
</div>
<p>
<b>Hint:</b> Drag and drop photos between the
two lists to add and remove photos from the
slideshow. Drag photos within the slideshow to
rearrange their order.
</p>
</div>
<%= drop_receiving_element(”slideshow-contents”,
:update => “slideshow-thumbs”,
:url => {:action => “add_photo” },
:accept => “photos”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’sortable_thumbs’)
) %>
This file has been almost entirely rewritten, so there are no marked-as-changed lines. You can see that I have laid out this edit page into three sections:
<div id=’slideshow-contents’> . . . </div>
<div id=’slideshow-photo-picker’> . . . </div>
<div id=’slideshow-attributes’> . . . </div>
Only the slideshow-photo-picker is new. It shows the list of unused photos that can be added to the slideshow. We will set up the CSS stylesheet to display these sections side-by-side as you saw them in Figure 6-4.
slideshow-contents is rendered by the partial template show_slides_draggable,
slideshow-photo-picker is rendered by the partial template photo_picker, and slideshow-attributes is mostly rendered by the form partial template that was generated from the scaffolding. I say “mostly” because I added a few things inline around the rendering of form.
Finally, notice two Ajax related helpers: drop_receiving_element and observe_field. We’ll come back to these in a little bit after we have discussed some prerequisite details.
Now, make these changes to photos/app/controllers/slideshows_controller.rb, replacing the edit method and creating the unused_photos method:
def edit
@slideshow = Slideshow.find(params[:id])
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
end
def unused_photos(slideshow)
all_photos = Photo.find(:all)
candidates = []
for photo in all_photos
in_slideshow = false
for slide in slideshow.slides
if slide.photo.thumbnail === photo.thumbnail
in_slideshow = true
break
end
end
candidates << photo if not in_slideshow
end
return candidates
end
The purpose of this code is to retrieve all the data needed by the edit.rhtml view template:
@slideshow = Slideshow.find(params[:id])
The id of the slideshow that you want to edit is passed in the request parameters from the browser. Here you retrieve that id and read that slideshow from the database, which you store in the instance variable @slideshow to make it available to the view template.
session[:slideshow] = @slideshow
Ajax actions requests will be coming in as the user makes changes, and you need to know what slideshow to change. This line saves a reference to the slideshow in the session hash. I’m using a key value of :slideshow to save and retrieve this from the session, but that value is arbitrary and could have been any unique identifier.
@photos = unused_photos(@slideshow)
This line calls the new method unused_photos to retrieve a list of all photos that are not in the slideshow; it then saves that list in @photos.
def unused_photos(slideshow)
This method returns a list of photos that are not in the slideshow. The logic should be self-explanatory. First, create an empty array (candidates = []), and then iterate through the list of all photos, adding them to the array (candidates << photo) if they are not already in the slideshow. The technique used here is grossly inefficient, but it will suffice for our purposes.
We still need to create the photo_picker template that generates the HTML to display all the photos that can still be added to a slideshow, so go ahead and create the file photos/app/views/slideshows/_photo_picker.rhtml with this in it:
<% for photo in @photos %>
<%= image_tag(”photos/#{photo.thumbnail}”,
:style => “vertical-align: middle”,
:id => “photo_#{photo.id}”,
:class => “photos”) %>
<%= draggable_element “photo_#{photo.id}”, :revert => true %>
<% end %>
This template iterates through the list of photos in @photos. For each photo, it uses the image_tag helper to create an HTML image tag and the draggable_element helper to generate the JavaScript code that makes it draggable. You can see that the first parameter of draggable_element matches the value of the id attribute (:id => “photo_#{photo.id}”) on the image tag. The draggable_element helper expects the id of the HTML element that it should make draggable, followed by zero or more options. The single option used here (:revert => true) says to move the element back to its original position after it is dropped.
But where can these draggable images be dropped? Recall that at the end of the slideshow’s edit.rthtml template we had:
<%= drop_receiving_element(”slideshow-contents”,
:update => “slideshow-thumbs”,
:url => {:action => “add_photo” },
:accept => “photos”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’sortable_thumbs’)
) %>
Just like the draggable_element helper, the drop_receiving_element helper expects the ID of the HTML element onto which you can drop something that was declared as draggable. The remaining parameters are options that given as name/value pairs (the order is not important). These options are doing a lot, so let’s go through them one at a time:
:update => “slideshow-thumbs”
This gives the ID of the HTML element that should be updated when a photo is dropped on our slideshow-contents div. The :position and :url options say how, and with what, that HTML element should be updated. When the :position option is omitted (as it is here), the HTML returned from the server replaces the target elements HTML. The :position option says that the returned HTML should be inserted into target element, instead of replacing it. The value :position can be specified as :before, :top, :bottom, and :after.
:url => {:action => “add_photo” }
This option constructs the URL that is sent to the server (via a background Ajax request) when a photo is dropped (you’ve seen this before). This executes the add_photo method in the current controller (the SlideshowsController). The add_ photo action adds the dropped photo to the slideshow and returns an HTML fragment that will replace the existing HTML in the target element, which, as you will see, is a rerendering of the slideshow’s contents, which now include the added photo.
:accept => “photos”
Without this option, you could drop any draggable element here. However, this line says that only HTML elements that have the class attribute “photos” can be dropped here. Remember that in our photo picker template we gave each photo class attribute of “photos”.
:droponempty => “true”
This option says that the user can drop photos here even if the target is completely empty.
:loading => visual_effect(:fade)
:complete => visual_effect(:highlight, ’sortable_thumbs’)
:loading and :complete (plus a few more events) specify client-side JavaScript event handlers that are executed at specific points in the progress of the Ajax request. In both cases, we are displaying a visual effect that gives the user positive feedback. The :loading event occurs when the browser begins loading the response, and the :complete event occurs when its all finished. The code specifies that the dropped photo will fade until it becomes invisible. It also highlights the target area on which the photo was dropped.
Adding a Dropped Photo
Now we need to create the add_photo method to actually add a dropped photo to the slideshow. Edit photos/app/controllers/slideshows_controller.rb, and add this:
def add_photo
slideshow_id = session[:slideshow].id
photo_id = params[:id].split(”_”)[1]
slide = Slide.new()
slide.photo_id = photo_id
slide.slideshow_id = slideshow_id
if !slide.save
flash[:notice] = ‘Error: unable to add photo.’
end
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
render_partial ’show_slides_draggable’
end
Let’s walk through this code:
slideshow_id = session[:slideshow].id
This line retrieves the current slideshow from the session hash and gets the slideshow’s id.
photo_id = params[:id].split(”_”)[1]
The id attribute of the dropped photo get passed as the :id parameter. If you recall from the photo_picker template, we set those ids to values such as “photo_1″ and “photo_19″, so the remainder of this line of code splits the string on the underscore, grabs the second half, and assigns it to photo_id.
The next five lines create a new slide, assign to it the photo id and the slideshow id, and then save it to the database.
Finally, we render and return the show_slides_draggable partial, after setting @slideshow to the current slideshow (which is needed by the partial template).
All that code handles dragging new photos to add to the slideshow. Now we just need to add a little more code to implement dragging a photo from the slideshow to the unused photos list as an intuitive way to remove photos from the slideshow.
The displayed list of photos in the slideshow are already draggable because we made them into a sortable list. The only problem with the current implementation is that the photos can be dragged vertically only. They need to be dragged both vertically for reordering and horizontally to the unused photos column.
We can drag the photos only vertically because the default option for a sortable list is :constraint => ‘vertical’. Fortunately, you can change this by editing the file photos/app/views/slideshows/_show_slides_draggable.rhtml and changing the call to the sortable_element helper to add this :constraint option:
<%= sortable_element(’sortable_thumbs’,
:url => {:action => ‘update_slide_order’},
:constraint => ”) %>
Now you can drag those photos anywhere. But you still need to make the unused photos list into a drop receiver that uses Ajax to remove the dropped photo from the slideshow.
To do so, edit photos/app/views/slideshows/edit.rhtml, and add this at the end:
<%= drop_receiving_element(”slideshow-photo-picker”,
:update => “slideshow-photos”,
:url => {:action => “remove_slide” },
:accept => “slides”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’slideshow-photos’)
) %>
This code is almost identical to the other drop_receiving_element we used. The difference is that the target is the slideshow-photo-picker, and the action taken on a drop is to call the remove_slide method. Also, notice that you can drop only “slides” here (that is, HTML elements with a class attribute of slides). If you go back and take a look at how we defined the partial template photos/app/views/ slideshows/_show_slides_draggable.rhtml, you will see that we did, indeed, make each item in the sortable list a slide.
Add the remove_slide method to photos/app/controllers/slideshows_controller.rb:
def remove_slide
slideshow_id = session[:slideshow].id
slide_id = params[:id].split(”_”)[1]
Slide.delete(slide_id)
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
render_partial ‘photo_picker’
end
In this code, you get the id of slide you want to remove, and then delete it from the slide database table. Remember, this action does not delete the photo from the database. The slide data says what photos are in a given slideshow, and deleting an entry from the slide table removes that slide from its slideshow. Finally, you render the HTML for the photo picker, which now includes the removed slide.
I’ll bet you’re anxious to see all this in action. All you need to do is to update the style sheet and then try it out. Edit photos/public/stylesheets/slideshows.css, and add the following:
#slideshow-photo-picker {
float: left;
width: 10em;
text-align: center;
border-right: thin solid #bbb;
padding: 0.50em;
padding-bottom: 10em;
}
img.thumbnail {
border: 2px solid black;
margin-bottom: 1em;
}
img.photos {
border: 2px solid black;
margin-bottom: 1em;
}
Whew! That’s it: try it now!
The first thing you’ll notice is that the Unused Photos section is empty (see Figure 6-5). That’s because all the photos are currently in the slideshow. Just drag a few of the slides out of the slideshow and drop them into the Unused Photos column; then you’ll have something more like Figure 6-6.
Filtering by Category
Displaying all unused photos might seem acceptable right now, but we have only nine photos. If there were 900, it would quickly become unusable. So, our final feature in this chapter will be to display only the unused photos in a particular category.
The first thing to do in our controller is get a list of all categories that can populate the drop-down selection box. Edit photos/app/controllers/slideshows_controller.rb, and add this line to the end of the edit method:
@all_categories = Category.find(:all, :order=>”name”)
This line retrieves a list of categories that can populate a drop-down selection box that the user will use to display only those unused photos that are in the selected category.
Figure 6-5. Drag and drop add and remov
Now, edit photos/app/views/slideshows/edit.rhtml, and add this right after the ‘Play this Slideshow’ line:
<p>
<label for=”category_id”>Filter “Unused Photos” on this Category</label><br/>
<%= collection_select(:category, :id, @all_categories, :id, :long_name) %>
<%= observe_field(:category_id,
:frequency => 2.0,
:update => ’slideshow-photos’,
:url => { :action => ‘change_filter’},
:with => ‘category_id’ ) %>
</p>
The collection_select helper is normally used inside an HTML form, but here we are using it because it conveniently knows how to display a collection in a drop-down box. It will never be submitted as part of a form.
As shown, the observe_field helper checks the category drop-down box for changes every two seconds. When a change is detected, an Ajax request is fired off to the change_filter method, which returns new HTML (that has been appropriately
filtered) to replace the slideshow-photos section.
Figure 6-6. Some unused photos
The Category model class automatically shows a collection of all photos that are in a particular category. However, we need to get a collection of photos that are in a given category and in all of its child categories.
Edit photos/app/models/category.rb, and add this method:
def photos_including_child_categories
result = photos.clone
children.each do |c|
c.photos_including_child_categories.each {|p|
result << p if not result.include? p}
end
result
end
This method recursively collects a list of all photos in its own category and all of its child categories. You can use this in to get the list of unused photos to display.
In the meantime, edit photos/app/controllers/slideshows_controller.rb to add the change_filter method:
def change_filter
slideshow_id = session[:slideshow].id
category_id = params[:category_id] || 1
session[:category_id] = category_id
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
render_partial ‘photo_picker’
end
This method stores the chosen category id in the session hash, retrieves a new list of unused photos, and then renders the photo_picker. Notice the bold code line in the previous code. This line tries to retrieve the category id from the request parameters. If there aren’t any parameters, params[:category_id] returns nil, and the || operator returns the rightmost argument (”1″ in this case).
Also, in this slideshow controller, we need to update the method that retrieves the unused photos to pay attention to the category setting. Do so by editing the unused_ photos method; then replace the line all_photos=Photo.find(:all) with the following:
category_id = session[:category_id] || 1
session[:category_id] = category_id
category = Category.find(category_id)
all_photos = category.photos_including_child_categories
We’re done; we’ve added category filtering! Fire up your browser, and try it (you may need to assign some categories to some unused photos). Now it looks like Figure 6-7.
Figure 6-7. Filtering on categories
We’ve come a long way in a very short time. With fewer than 200 lines of code, we’ve added drag-and-drop capability to add and reorder slides. We’ve also added the core capability to actually show a slideshow. Ajax made our application much easier to use and more attractive. Next, we’ll look into testing this application.
—
by O’Reilly Media
20 Oct
Posted by ProCOM
on October 20, 2007 – 1:23 pm - 1,941 views
In this first article in a two-part series, you will learn how Ruby on Rails implements Ajax. We will use the specific example of a slideshow to demonstrate our points. This article is excerpted from chapter six of the book Ruby on Rails: Up and Running, written by Bruce A. Tate and Curt Hibbs (O’Reilly, 2006; ISBN: 0596101325). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
Ajax is one of the most important emerging trends in web applications. Web sites like Google Maps and Gmail dramatically demonstrate that web applications do not have to be slow, clunky, page-at-a-time web forms. Ajax techniques can reclaim some of the fluidity and responsiveness that was lost when we moved from desktop applications to web applications.
Ajax (which stands for the cryptic “Asynchronous JavaScript and XML”) is a technique for building web pages that are more interactive, exciting, and dynamic. Ajax is asynchronous: JavaScript libraries can communicate with the server at any time, and the web page need not be frozen while waiting for a response. Ajax uses JavaScript on the browser, any language on the server, and XML to specify messages.
When you use this emerging technique, a web page can communicate with the server at any time, updating only those portions of the display that need it. Users experience more responsive web pages, with immediate feedback. Even though using Ajax techniques usually requires significantly more sophisticated design and implementation skills, the benefits to the end user are so great that Ajax-enabled web applications will soon become the rule, not the exception. Fortunately, Rails makes Ajax so simple that, for typical cases, using Ajax is almost as easy as not using it.
How Rails Implements Ajax
Rails has a simple, consistent model for how it implements Ajax operations. Once the browser has rendered and displayed the initial web page, different user actions cause it to display a new web page (like any traditional web application) or trigger an Ajax operation:
Some trigger fires
This trigger could be the user clicking on a button or link, the user making changes to the data on a form or in a field, or just a periodic trigger (based on a timer).
The web client calls the server
A JavaScript method, XMLHttpRequest, sends data associated with the trigger to an action handler on the server. The data might be the ID of a checkbox, the text in an entry field, or a whole form.
The server does something
The server-side action handler–a Rails controller action (for our purposes)–does something with the data and returns an HTML fragment to the web client.
The client receives the response
The client-side JavaScript, which Rails creates automatically, receives the HTML fragment and uses it to update a specified part of the current pages HTML, often the content of a <div> tag.
These steps are the simplest way to use Ajax in a Rails application, but with a little extra work, you can have the server return any kind of data in response to an Ajax request, and you can create custom JavaScript in the browser to perform more involved interactions. We’ll stick to HTML fragments in this chapter.
Rails uses the Prototype and script.aculo.us JavaScript libraries to implement browser support for Ajax. You can use these libraries independently of Rails, but with their seamless integration with Rails, you probably wont want to. Throughout this chapter, we’ll exploit the Ajax and special-effects capabilities that come with Rails to implement missing features in our Photo Share application.
Playing a Slideshow
Let’s see what happens when we try to play a slideshow. Browse to http://127.0.0.1: 3000/slideshows/list, and click the Play link for our only slideshow. As you can see in Figure 6-1, this URL invokes the show action on the slideshow controller, but the action is still using the scaffold code.
We need to change this page to actually “play” the slideshow by sequentially displaying the pictures contained in the slideshow. To do this, we will initially display the first picture in the slideshow; then, once every two seconds, we’ll make an Ajax call to get and display the next picture.
The controller sets up all the slides in a slideshow for playback. You need to start @slideshow with the current slideshow set to play. You also need to put the current slide (initially, 0) and the whole slideshow into a holding area called the session, so you won’t have to read from the database each time you play a new slide. Edit the slideshows controller (app/controllers/slideshows_controller.rb), and modify the show method to look like this:
def show
@slideshow = Slideshow.find(params[:id])
session[:slideshow] = @slideshow
session[:slide_index] = 0
Figure 6-1. Playing a slideshow that still uses scaffolding
@slide = @slideshow.slides[0]
end
Every two seconds in this code, the browser sends an Ajax request to get the next slide. You can’t use instance variables to keep track of where you are in the slideshow because instance variables exist only until you finish processing the current request. Use the Rails-provided session object instead, which is persistent across requests. Let’s look at this code in a little more detail.
session[:slideshow] = @slideshow stores a reference to the current slideshow in the session hash at the key :slideshow. We do the same thing with the index of the current slide that is being played. We initially set the slide_index to zero to point to the first slide, and our Ajax request increments the index by one as it displays each slide. We can retrieve these values from the session hash during the Ajax requests for the next slide.
Now, edit the view template (app/views/slideshows/show.rhtml) to look like this:
<p><i><%= @slideshow.name %></i></p>
<div id=”slides”>
<%= render :partial => “show_slide” %>
</div>
<%= periodically_call_remote :update => ’slides’,
:url => { :action => :show_slide },
:frequency => 2.0 %>
This RHTML template contains three things: a title line, the div that displays the current slide, and a magic Ajax incantation that we will now pick apart.
The periodically_call_remote Rails helper function creates JavaScript that periodically sends a request to the server and uses the HTML fragment that is returned to replace the content of the update target. In this case, the update target is an HTML element with an ID of ’slides’, which is a <div> tag. The returned HTML fragment replaces the contents of this <div> tag. The URL that makes the request is constructed to ensure that it will be routed to the show_slide method of the current controller (the slideshows controller). Finally, the frequency parameter makes the call once every two seconds. All Ajax help functions take their parameters in key/value pairs, so you can list the parameters in any order.
We need to display each slide as it comes back to the client; Rails uses a partial HTML template to do this work. To create the partial view template, place the following contents in a new file called app/views/slideshows/_show_slide.rhtml:
<%= image_tag “photos/#{@slide.photo.filename}” %>
<p><%= @slide.photo.filename %></p>
And its controller method in app/controllers/slideshows_controller.rb:
def show_slide
@slideshow = session[:slideshow]
session[:slide_index] += 1
@slide = @slideshow.slides[session[:slide_index]]
if @slide == nil
session[:slide_index] = 0
@slide = @slideshow.slides[0]
end
render :partial => “show_slide”
end
This method retrieves the slideshow information from the session, moves to the next slide (or back to the beginning if at the end), and then explicitly renders the partial view template show_slide. You need to render a partial view or render with the option :render_layout => false . Otherwise, Rails tries to render a full template, including layout. As our page already has a layout, simply render a partial template, consisting of an image tag for the slide, and its name.
Finally, you need to update your standard layout template to include script tags for the Prototype JavaScript library because the client-side JavaScript code that Rails creates for you uses them, so in app/views/layouts/standard.rhtml, insert this line immediately after the title tags:
<%= javascript_include_tag ‘prototype’, ‘effects’, ‘dragdrop’ %>
This line includes three JavaScript files that are shipped with Rails: prototype.js and two Script.aculos.us files, effects.js and dragdrop.js. We will use these last two shortly.
Now show a slideshow by loading slideshows/list and clicking Show, and you will see the actual pictures in the slideshow, changing every two seconds.
Using Drag-and-Drop to Reorder Slides
The scaffolding we have for editing a slideshow shows just the slideshow attributes that are stored directly in the slideshows table: the slideshows name and the date on which it was created. The most important part is missing: the photos that are part of the slideshow!
By now, you’ve probably realized that this is because the scaffolding code deals with only one database table: the slideshows table. The relationship data about which photos are assigned to a slideshow and their order in the slideshow are stored in the slides table. Scaffolding does not handle relationships, so you have to write the code to edit this relationship data.
We’re going to display a list of thumbnails of all the photos that are in a slideshow, and then let the user reorder them using drag-and-drop. If you’ve had to struggle through implementing drag-and-drop before, you’re not going to believe how easy this is going to be. Here’s a hint: this will take a total 34 additional lines of Ruby, CSS, and RHTML template!
Let’s start by reviewing the current implementation of the edit action in the slideshow controller:
def edit
@slideshow = Slideshow.find(params[:id])
end
This action expects to find the ID of the slideshow to edit passed in as the id parameter, which is normally decoded from the URL. You find the slideshow with that ID and assign that slideshow object to the instance variable @slideshow, so that it can be accessed in the view template.
That is really all that’s needed here, so you won’t have to add any code to this method. The changes will start with the edit view template, so edit the template photos/app/views/slideshows/edit.rhtml and make it look like this (the changes are in bold):
<h1>Editing slideshow</h1>
<%= link_to ‘Play this Slideshow’,
:action => ’show’, :id => @slideshow %>
<div id=’slideshow-contents’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
<div id=’slideshow-attributes’>
<%= start_form_tag :action => ‘update’, :id => @slideshow %>
<%= render :partial => ‘form’ %>
<%= submit_tag ‘Save Attributes’ %>
<%= end_form_tag %>
</div>
Notice that the existing <%= render :partial => ‘form’ %> is wrapped in a <div> tag with an id attribute of slideshow-attributes. You will use this name in one of your CSS files to control how this section is displayed.
There is also a completely new section that displays thumbnails of the photos in the slideshow:
<div id=’slideshow-contents’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
This code also uses a <div> tag with an id attribute, for the same reason: to use a CSS file to control its appearance. This div also renders a new partial view template named show_slides_draggable, which we will create next.
Create the file photos/app/views/slideshows/_show_slides_draggable.rhtml with the following contents:
<ol id=’sortable_thumbs’>
<% for slide in @slideshow.slides %>
<li id=’thumbs_<%= slide.id %>’ class=’slides’>
<%= thumbnail_tag slide %>
</li>
<% end %>
</ol>
<%= sortable_element(’sortable_thumbs’,
:url => {:action => ‘update_slide_order’}) %>
The first part is pretty standard stuff. We’re creating an HTML ordered list, in which each list item is a thumbnail image of one of the photos in the slideshow (note that the thumbnail_tag helper function that was created earlier). However, it’s the last two lines that do the heavy lifting.
sortable_element is a helper function that generates the JavaScript code that turns our list into a user-sortable, drag-and-drop-capable list. It wraps this list an HTML form, and the :url option specifies the URL to post to the server whenever the user changes the order of the list. In this case, it calls the action method update_slide_order in our slideshow controller. This call works in the background using an Ajax call.
The update_slide_order method is pretty simple as well. Edit photos/app/controllers/ slideshows_controller.rb, and add this method:
def update_slide_order
params[:sortable_thumbs].each_with_index do |id, position|
Slide.update(id, :position => position)
end
end
This method iterates through each slide in the list, extracting its ID and position in the list, and uses this information to update that slide’s database row with its new position. Let’s walk through this code in a little more detail:
We’re almost ready to give it a try, but first let’s edit photos/public/stylesheets/slideshows.css and add some formatting instructions for the two div IDs we created. Add the following at the end of the file:
#slideshow-contents {
float: left;
width: 11em;
padding: 0.50em;
text-align: center;
border-right: thin solid #bbb;
padding: 0.50em;
padding-bottom: 10em;
}
#slideshow-attributes {
margin-left: 23em;
padding-left: 1.5em;
padding-top: 1.5em;
}
This causes the contents of the slideshow (which will be a list of thumbnail images) to be displayed down the left side of the page, and the slideshow’s attributes will be displayed immediately to the right of the thumbnails.
Let’s see how this looks. Browse to http://127.0.0.1:3000/slideshows/list, and click the edit link for our one and only slideshow. It will look like Figure 6-2.
Click on one of the photos, and try dragging it around. When you drop it into a new location, update_slide_order is called to write the new order to the database.
Lets fix one minor thing here before we move on. Wouldn’t it be better to see the number of each photo appear vertically aligned in the middle of the thumbnail instead of at the bottom? Because the HTML for each thumbnail image is created by…
Figure 6-2. A drag-and-drop list of photos
…
our own helper function, thumbnail_tag, we just need to edit that function and add a vertical-align style attribute.
First, edit photos/app/helpers/slideshows_helper.rb, and add the code shown in bold:
module SlideshowsHelper
def thumbnail_tag(slide)
image_tag(”photos/#{slide.photo.thumbnail}”,
:style=>”vertical-align:middle”) if slide
end
end
Now, refresh your browser: the list numbers are nicely centered, as you can see in Figure 6-3.
With a very small amount of code, we added a very nice drag-and-drop user interface for reordering the slides in a slideshow. But we’re just getting started with our Ajax-enabled user interface.
Figure 6-3. Nicely centered list numbers
Please check back tomorrow for the conclusion to this article.
—
by O’Reilly Media
Java and Flash offer real alternatives for those trying to build web applications that function more like desktop applications. However, they have their shortcomings. AJAX overcomes these shortcomings — especially if you use Ruby on Rails.
Interactivity and responsiveness has been considered the USP of desktop applications and not of web applications. Even though there exist mechanisms such as Java Applets and Flash files that provide interactivity and responsiveness up to a point, they are intrusive. That is, the user needs to install either a Java or a Flash plug-in.
However, with AJAX coming into the foreground, a non-intrusive way of providing interactive and responsive web applications has evolved. Even with AJAX, the problem is not fully solved for developers. The reason for this is the peripheral integration of AJAX in almost all the existing frameworks. Still, there are exceptions in the form of frameworks that provide AJAX-based functionalities as their core services. Ruby-on-Rails (RoR) is one of them.
In this discussion, I will be focusing on using AJAX with RoR. The first section will be a brief overview of AJAX. In the second section, I will detail the steps involved in AJAXifying the RoR application. In the last section, I will be developing a real world application using AJAX and RoR. That’s the agenda for this discussion.
AJAX: an Overview
By definition AJAX is “a technique that extends the traditional web application model to allow for in-page server requests.” In other words, AJAX provides the functionality to make on the fly requests to the server and present the response data to the user by modifying a part of the page without refreshing the whole page. AJAX is not a single technology; rather, it’s a combination of technologies. These technologies form the basic components of AJAX, which are:
The second component is an object of JavaScript itself. However the first ‘A’ of AJAX comes from it. The reason for this is detailed below.
JavaScript is the “J” in AJAX. It is the JavaScript that parses the response from the server and makes appropriate changes to the current page. Using JavaScript, one can change a paragraph, replace a product image or change the complete look and feel of the page without the intervention of the server. This reduces the bandwidth and makes the web site look more responsive to the user.
XMLHttpRequest is an object of JavaScript that not only allows the user to create or construct HTTP calls from within the script but also process the response sent by the server. It is available as an ActiveX control in Internet Explorer and as a JavaScript object in other browsers. Though it is known as XMLHttpRequest, one can wrap any kind of HTTP communication using it.
The first ‘A’ in AJAX, which stands for “Asynchronous,” comes at the “behest” of XMLHttpRequest. One of the abilities of XMLHttpRequest is to make a call to the web server in the background while the user continues to do his/her work on the UI. Whenever the response is received, the XMLHttpRequest accepts it and passes it over to the function that is registered to handle the response. Thus the request-response cycle is relieved off the paradigm of synchronous communication.
If one looks up the definition of XML on the net, the most common definition found is this: “XML (Extensible Markup Language) is a W3C initiative that allows information and services to be encoded with meaningful structure and semantics that computers and humans can understand and is great for information exchange, and can easily be extended to include user-specified and industry-specified tags.” The extensibility and the ability to provide data in a structured format is the main reason for XML to be the preferred response data format sent by the server. This doesn’t mean that the server can’t send data in any other format. It can send data in any format and XMLHttpRequest can accept and process it. However, since XML is a standard that is both extensible and structured, it is preferred over every other.
The next obvious question is how these three components fit in the overall picture. Here is an example of AJAX at work. It checks the validity of the username.
var xmlHttp;
function createXmlHttpRequest()
{
//check the type of browser and initialize XMLHttpRequest object
if(window.ActiveXObject)
{
xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”);
}
else if(window.XMLHttpRequest)
{
xmlHttp=new XMLHttpRequest();
//alert(”inside elseif…”+xmlHttp);
}
}
//This method creates an instance of XMLHttpRequest and calls the server
//Then delegates the data processing to another function on receipt of data
function startRequest()
{
createXmlHttpRequest();
var u1=document.f1.user.value;
xmlHttp.open(”GET”,”validate.php?user=”+u1,true)
xmlHttp.onreadystatechange=handleStateChange;
xmlHttp.send(null);
}
//Does the handling of data and presentation of data
function handleStateChange()
{
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
document.getElementById(”results”).innerHTML= xmlHttp.responseText;
}
else
{
alert(”Error loading pagen”+ xmlHttp.status +”:”+ xmlHttp.statusText);
}
}
}
function resetDisplay()
{
document.getElementById(”results”).innerHTML=” ”
}
The logic here is very simple, yet the amount of boilerplate code required is pretty high and the complexity of JavaScript increases with the increase in complexity of the logic of data processing and presentation. And I have not shown the logic at server-side. This is the story with almost all of the technology except Ruby-on-Rails. How RoR is different from the others is the topic of the next section.
Implementing AJAX
In the last section, the example demonstrated the complexity involved in AJAXifying an application having even the simplest logic to be implemented. So how can RoR make it simple? RoR does it by providing AJAX as one of its core functionalities. RoR has the prototype, effects, dragdrop and controls JavaScript libraries as built-in libraries. Secondly, a helper called JavaScriptHelper provides wrapping around JavaScript code so that switching between Ruby and JavaScript won’t be necessary. So RoR makes things simpler by wrapping JavaScript libraries and providing them as built-in RoR libraries.
Now let’s look at ways to use AJAX within RoR. To implement AJAX, RoR provides two basic ways which are:
The former is used with non-form HTML whereas the later is used with form and form based HTML elements.
The link_to_remote() is one of the simplest yet versatile and flexible helpers in RoR. It takes following parameters:
So what are the steps required to use link_to_remote()? The steps are as follows:
The steps are common to that of any typical RoR. That is the beauty of RoR. Of these steps, the third step is optional if rendering is taken care of by the action itself.
<%= javascript_include_tag “prototype” %>
Here is an example of a page having link_to_remote():
<html>
<head>
<title>Ajax Demo</title>
<%= javascript_include_tag “prototype” %>
</head>
<body>
<h1>What time is it?</h1>
<div id=”time_div”>
I don’t have the time, but
<%= link_to_remote( “click here”,
:update => “time_div”,
:url =>{ :action => :say_when }) %>
and I will look it up.
</div>
</body>
</html>
The link_to_remote() is passed three arguments: the text to be shown i.e. “click here”, the id of the link created which is “time_div” and the action to be called which is say_when. The next step is to implement the controller containing the action.
class DemoController < ApplicationController
def index
end
def say_when
render(:layout => false)
end
end
The render function is called with the layout option as false because only a part of the HTML page is being updated; hence there is no requirement of any Rails layout wrappers. The example of the second type is as follows:
class DemoController < ApplicationController
def index
end
def say_when
render_text “<p>The time is <b>” + DateTime.now.to_s + “</b></p>”
end
end
It uses render_text method to generate the response.
<p>The time is <b> <%=DateTime.now.to_s%>
</b></p>
That’s how link_to_remote() is used. Next let’s see how form_remote_tag() is used.
While dealing with links, link_to_remote() is handy but what about form elements? That’s where form_remote_tag() comes in. One can easily enable any Rails form to use AJAX using form_remote_tag(). It serializes and sends all the form elements to the server via XMLHttpRequest. All this is done automatically without any requirement for extra logic implementation. The form_remote_tag() takes three parameters:
To use the form_remote_tag() there are three steps:
The steps are similar to those of using link_to_remote(). Only the way they are implemented is different.
<html>
<head>
<title>Ajax List Demo</title>
<%= javascript_include_tag “prototype” %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<%= form_remote_tag(:update => “my_list”,
:url => { :action => :add_item },
:position => “top” ) %>
New item text:
<%= text_field_tag :newitem %>
<%= submit_tag “Add item with Ajax” %>
<%= end_form_tag %>
<ul id=”my_list”>
<li>Original item… please add more!</li>
</ul>
</body>
</html>
Here the id of the element is given as my_list which is the id of the list that will be populated by the response provided by the action. The url specifies the action to be called and the position tells Rails to place the returned HTML snippet on the top of the element whose id has been specified in the update parameter. Now let’s look at the action.
class ListdemoController < ApplicationController
def index
end
def add_item
render_text “<li>” + params[:newitem] + “</li>”
end
end
whereas if the rendering is to be done by a template then the code would be:
class ListdemoController < ApplicationController
def index
end
def add_item
@item= params[:newitem]
render(:layout => false)
end
end
The only difference is that the parameter passed by form_remote_tag() is placed in a variable called item which can be used in the template.
<li> <%=@item%> </li>
That’s it.
So now you have seen how easy the steps are for creating an AJAX based application in RoR. Next I will develop a real world application using RoR and AJAX.
Ruby-on-Rails and AJAX
In real world applications one of the most required functionalities is to populate a combo box based on the data entered in a preceding field such as a text box or another combo box. In this example, I will be developing such a part for an application. This can be used with any application with slight modifications. It contains two components:
index.rhtml – the view which contains the combo box to be filled and the
combo box on the basis of which the filling has to be donePopulateController – the controller containing the action required for the
populating of the combo box.
Here is the index.rhtml:
<html>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=utf-8″ />
<%= javascript_include_tag “prototype” %>
<title>Ajax Rails & Select lists</title>
</head>
<body>
<%= form_remote_tag(:update => “sel_con”,:url => {
:action => :create_select },
:position => “top”,:success => “$(’sel_con’).innerHTML=”” ) %>
<p>
Please select a sports category:
</p>
<p>
<%= select_tag “categories”,
“<option>Team</option><option>Individual</option>” %>
</p>
<div id=”sel_con”></div>
<p>
<%= submit_tag “Show Sports” %>
</p>
<%= end_form_tag %>
</body>
</html>
The combo box contains two values, team and individual. Based on the selection the second combo has to be populated and shown at the div having the id sel_con. If team is selected by the user, the second combo box would be filled with team events whereas in the case of individual, individual event is populated. The form_remote_tag is passed four parameters: the id of element to be updated, the action to be called, the position in which to place the result and what has to be done if the request is successfully executed. In this case the inner HTML is set to no value i.e. “”. Next is the controller:
class PopulateController < ApplicationController
def index
end
def create_select
indArr=["Nordic Skiing", "Inline Skating","Tennis",
"Triathlon","Road Racing","Figure Skating",
"Weight Lifting","Speed Skating","Snowboarding"];
teamArr=["Soccer","Basketball","Football","Hockey",
"Baseball","Lacrosse"];
str=”";
if params[:categories].index(’Team’) != nil
render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}
elsif params[:categories].index(’Individual’) != nil
render :partial => “options”,
:locals => { :sports => indArr, :sptype => “individual” }
else
str=”<select id=’individual’ name=’individual’>
<option>unknown</option></select>”;
render :text => str;
end
#end method
end
#end class definition
end
The create_select is the action where the logic for creating and populating the second combo box goes. First it creates two arrays with team and individual sports events. Then based on the parameter received, the combo box is created and populated. This is done in the statement
render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}
Now let’s look at _options.rhtml which is a partial template i.e. the a piece of HTML code that can be used again and again. Here is the code:
<select id=”<%= sptype %>” name=”<%= sptype %>”>
<% sports.each do |sport| %>
<option><%= sport %></option>
<% end %>
</select>
It first gives the combo box an id, iterates through the array passed and populates the combo box. That’s it. This completes the application. Though I had mentioned four libraries, I have introduced only one of them. In the next discussion, I will be focusing on UI using the other three libraries. Till then..
—
by A.P.Rajshekhar
19 Oct
Posted by ProCOM
on October 19, 2007 – 1:04 pm - 874 views
In this conclusion to a six-part series covering web development and Ruby on Rails, you’ll learn how to send error messages to your email and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
15.20 Automatically Sending Error Messages to Your Email
Problem
You want to receive a descriptive email message every time one of your users encounters an application error.
Solution
Any errors that occur while running your application are sent to the ActionController::Base#log_error method. If you’ve set up a mailer (as shown in Recipe 15.19) you can override this method and have it send mail to you. Your code should look something like this:
class ApplicationController < ActionController::Base
private
def log_error(exception)
super
Notification.deliver_error_message(exception,
clean_backtrace(exception),
session.instance_variable_get(”@data”),
params,
request.env
)
end
end
That code rounds up a wide variety of information about the state of the Rails request at the time of the failure. It captures the exception object, the corresponding backtrace, the session data, the CGI request parameters, and the values of all environment variables.
The overridden log_error calls Notification.deliver_error_messsage, which assumes you’ve created a mailer called “Notification”, and defined the method Notification.error_message. Here’s the implementation:
class Notification < ActionMailer::Base
def error_message(exception, trace, session, params, env, sent_on = Time.now)
@recipients = ‘me@mydomain.com’
@from = ‘error@mydomain.com’
@subject = “Error message:
#{env['REQUEST_URI']}”
@sent_on = sent_on
@body = {
:exception => exception,
:trace => trace,
:session => session,
:params => params,
:env => env
}
end
end
The template for this email looks like this:
<!– app/views/notification/error_message.rhtml –>
Time: <%= Time.now %>
Message: <%= @exception.message %>
Location: <%= @env['REQUEST_URI'] %>
Action: <%= @params.delete(’action’) %> </td> </tr>
Controller: <%= @params.delete(’controller’) %> </td> </tr>
Query: <%= @env['QUERY_STRING'] %> </td> </tr>
Method: <%= @env['REQUEST_METHOD'] %> </td> </tr>
SSL: <%= @env['SERVER_PORT'].to_i == 443 ? “true” : “false” %>
Agent: <%= @env['HTTP_USER_AGENT'] %>
Backtrace
<%= @trace.to_a.join(”</p>\n<p>”) %>
Params
<% @params.each do |key, val| -%>
* <%= key %>: <%= val.to_yaml %>
<% end -%>
Session
<% @session.each do |key, val| -%>
* <%= key %>: <%= val.to_yaml %>
<% end -%>
Environment
<% @env.each do |key, val| -%>
* <%= key %>: <%= val %>
<% end -%>
Discussion
ActionController::Base#log_error gives you the flexibility to handle errors however you like. This is especially useful if your Rails application is hosted on a machine to which you have limited access: you can have errors sent to you, instead of written to a file you might not be able to see. Or you might prefer to record the errors in a database, so that you can look for patterns.
The method ApplicationController#log_error is declared private to avoid confusion. If it weren’t private, all of the controllers would think they had a log_error action defined. Users would be able to visit /<controller>/log_error and get Rails to act strangely.
See Also
15.21 Documenting Your Web Site
Problem
You want to document the controllers, models, and helpers of your web application so that the developers responsible for maintaining the application can understand how it works.
Solution
As with any other Ruby program, you document a Rails application by adding specially-formatted commands to your code. Here’s how to add documentation to the FooController class and one of its methods:
# The FooController controller contains miscellaneous functionality
# rejected from other controllers.
class FooController < ApplicationController
# The set_random action sets the @random_number instance variable
# to a random number.
def set_random
@random_number = rand*rand
end
end
The documentation for classes and methods goes before their declaration, not after.
When you’ve finished adding documentation comments to your application, go to your Rails application’s root directory and issue the rake appdoc command:
$ rake appdoc
This Rake task runs RDoc for your Rails application and generates a directory called doc/app. This directory contains a web site with the aggregate of all your documentation comments, cross-referenced against the source code. Open the doc/app/index.rhtml file in any web browser, and you can browse the generated documentation.
Discussion
Your RDoc comments can contain markup and special directives: you can describe your arguments in definition lists, and hide a class or method from documentation with the :nodoc: directive. This is covered in Recipe 17.11.
The only difference between Rails applications and other Ruby programs is that Rails comes with a Rakefile that defines an appdoc task. You don’t have to find or write one yourself.
You probably already put inline comments inside your methods, describing the action as it happens. Since the RDoc documentation contains a formatted version of the original source code, these comments will be visible to people going through the RDoc. These comments are formatted as Ruby source code, though, not as RDoc markup.
See Also
15.22 Unit Testing Your Web Site
Problem
You want to create a suite of automated tests that test the functionality of your Rails application.
Solution
Rails can’t write your test code any more than it can write your views and controllers for you, but it does make it easy to organize and run your automated tests.
Rails can’t write your test code any more than it can write your views and controllers for you, but it does make it easy to organize and run your automated tests.
When you use the ./script/generate command to create controllers and models, not only do you save time, but you also get a generated framework for unit and functional tests. You can get pretty good test coverage by filling in the framework with tests for the functionality you write.
So far, all the examples in this chapter have run against a Rails application’s development database, so you only needed to make sure that the development section of your config/database.yml file was set up correctly. Unit test code runs on your application’s test database, so now you need to set up your test section as well. Your mywebapp_test database doesn’t have to have any tables in it, but it must exist and be accessible to Rails.
When you generate a model with the generate script, Rails also generates a unit test script for the model in the test directory. It also creates a fixture, a YAML file containing test data to be loaded into the mywebapp_test database. This is the data against which your unit tests will run:
./script/generate model User
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate
create db/migrate/ 001_create_users.rb
When you generate a controller with generate, Rails creates a functional test script for the controller:
./script/generate users list
exists app/controllers/
exists app/helpers/
create app/views/users
exists test/functional/
create app/controllers/ users_controller.rb
create test/functional/ users_controller_test.rb
create app/helpers/
users_helper.r b
create app/views/users/list.rhtml
As you write code in the model and controller classes, you’ll write corresponding tests in these files.
To run the unit and functional tests, invoke the rake command in your home directory. The default Rake task runs all of your tests. If you run it immediately after generating your test files, it’ll look something like this:
$ rake
(in /home/lucas/mywebapp)
/usr/bin/ruby1.8 “test/unit/user_test.rb”
Started
.
Finished in 0.048702 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
/usr/bin/ruby1.8 “test/functional/users_controller_test.rb”
Started
.
Finished in 0.024615 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Discussion
All the lessons for writing unit tests in other languages and in other Ruby programs (see Recipe 17.7) apply to Rails. Rails does some accounting for you, and it defines some useful new assertions (see below), but you still have to do the work. The rewards are the same, too: you can modify and refactor your code with confidence, knowing that if something breaks, your tests will break. You’ll hear about the problem immediately and you’ll be able to fix it more quickly.
Let’s see what Rails has generated for us. Here’s a generated test/unit/user_test.rb:
require File.dirname(__FILE__) + ‘/../test_helper’
class UserTest < Test::Unit::TestCase
fixtures :users
# Replace this with your real tests.
def test_truth
assert true
end
end
A good start, but test_truth is kind of tautological. Here’s a slightly more realistic test:
class UserTest
def test_first
assert_kind_of User, users(:first)
end
end
This code fetches the first element from the users table, and asserts that ActiveRecord turns it into a User object. This isn’t testing our User code (we haven’t written any) so much as it’s testing Rails and ActiveRecord, but it shows you the kind of assertion that makes for good unit tests.
But how does users(:first) return anything? The test suite runs against the mywebapp_test database, and we didn’t even put any tables in it, much less sample data.
We didn’t, but Rails did. When you run the test suite, Rails copies the schema of the development database to the test database. Instead of running every test against whatever data happens to exist in the development database, Rails loads special test data from YAML files called fixtures. The fixture files contain whatever database data you need to test: objects that only exist to be deleted by a test, strange relation ships between rows in different tables, or anything else you need.
In the example above, the fixture for the users table was loaded by the line fixtures :users. Here’s the generated fixture for the User model, in test/fixtures/users.yml:
first:
id: 1
another:
id: 2
Before running the unit tests, Rails reads this file, creates two rows in the users table, and defines aliases for them (:first and :another) so you can refer to them in your unit tests. It then defines the users method (like so much else, this method name is based on the name of the model). In test_first, the call to users(:first) retrieves the User object corresponding to :first in the fixture: the object with ID 1.
Here’s another unit test:
class UserTest
def test_another
assert_kind_of User, users(:another)
assert_equal 2, users(:another).id
assert_not_equal users(:first), users(:another)
end
end
Rails adds the following Rails-specific assertions to Ruby’s Test::Unit:
See Also
15.23 Using breakpoint in Your Web Application
Problem
Your Rails application has a bug that you can’t find using log messages. You need a heavy-duty debugging tool that lets you inspect the full state of your application at any given point.
Solution
The breakpoint library lets you stop the flow of code and drop into irb, an interactive Ruby session. Within irb you can inspect the variables local to the current scope, modify those variables, and resume execution of the normal flow of code. If you have ever spent hours trying to track down a bug by placing logging messages everywhere, you’ll find that breakpoint gives you a much easier and more straightforward way to debug.
But how can you run an interactive console program from a web application? The answer is to have a console program running beforehand, listening for calls from the Rails server.
The first step is to run ./script/breakpointer on the command line. This command starts a server that listens over the network for breakpoint calls from the Rails server. Keep this program running in a terminal window: this is where the irb session will start up:
$ ./script/breakpointer
No connection to breakpoint service at druby://localhost:42531
Tries to connect will be made every 2 seconds…
To trigger an irb session, you can call the breakpoint method anywhere you like from your Rails application–within a model, controller, or helper method. When execution reaches that point, processing of the incoming client request will stop, and an irb session will start in your terminal. When you quit the session, processing of the request will resume.
Discussion
Here’s an example. Let’s say you’ve written the following controller, and you’re having trouble modifying the name attribute of an Item object.
class ItemsController < ApplicationController
def update
@item = Item.find(params[:id])
@item.value = ‘[default]‘
@item.name = params[:name]
@item.save
render :text => ‘Saved’
end
end
You can put a breakpoint call in the Item class, like this:
class Item < ActiveRecord::Base
attr_accessor :name, :value
def name=(name)
super
breakpoint
end
end
Accessing the URL http://localhost:3000/items/update/123?name=Foo calls Item-Controller#update, which finds Item number 123 and then calls its name= method. The call to name= triggers the breakpoint. Instead of rendering the text “Saved”, the site seems to hang and become unresponsive to requests.
But if you return to the terminal running the breakpointer server, you’ll see that an interactive Ruby session has started. This session allows you to play with all the local variables and methods at the point where the breakpoint was called:
Executing break point “Item#name=” at item.rb:4 in `name=’
irb:001:0> local_variables
=> ["name", "value", "_", "__"]
irb:002:0> [name, value]
=> ["Foo", "[default]“]
irb:003:0> [@name, @value]
=> ["Foo", "[default]“]
irb:004:0> self
=> #<Item:0×292fbe8 @name=”Foo”, @value=”[default]“>
irb:005:0> self.value = “Bar”
=> “Bar”
irb:006:0> save
=> true
irb:006:0> exit
Server exited. Closing connection…
Once you finish, type exit to terminate the interactive Ruby session. The Rails application continues running at the place it left off, rendering “Saved” as expected.
By default, breakpoints are named for the method in which they appear. You can pass a string into breakpoint to get a more descriptive name. This is especially helpful if one method contains several breakpoints:
breakpoint “Trying to set Item#name, just called super”
Instead of calling breakpoint directly, you can also call assert, a method which takes a code block. If the block evaluates to false, Ruby calls breakpoint; otherwise, things continue as normal. Using assert lets you set breakpoints that are only called when something goes wrong (called “conditional breakpoints” in traditional debuggers):
1.upto 10 do |i|
assert { Person.find(i) }
p = Person.find(i)
p.update_attribute(:name, ‘Lucas’)
end
If all of the required Person objects are found, the breakpoint is never called, because Person.find always returns true. If one of the Person objects is missing, Ruby calls the breakpoint method and you get an irb session to investigate.
Breakpoint is a powerful tool that can vastly simplify your debugging process. It can be hard to understand the true power of it until you try it yourself, so go through the solution with your own code to toy around with it.
See Also
————————————————-
* Python, for instance, has several excellent web application frameworks, but that’s just the problem. It has several, and a powerful community is fractured on the issue of which to use. Ruby has no major web application frameworks apart from Rails. In a sense, Ruby’s former obscurity is what made the dominance of Rails possible.
* You could throw an exception, but then your redirect wouldn’t happen: the user would see an exception screen instead.
* More precisely, our models have been embedded in our controllers, as ad hoc data structures like hardcoded shopping lists.
* The helper function time_ago_in_words() calculates how long it’s been since a certain time and returns English text such as “about a minute” or “5 hours” or “2 days”. This is a nice, easy way to give the user a perspective on what a date means.
* Rails extends Ruby’s numeric classes to include some very helpful methods (like the hour method shown here). These methods convert the given unit to seconds. For example, Time.now + 1.hour is the same as Time. now + 3600, since 1.hour returns the number of seconds in an hour. Other helpful methods include minutes, hours, days, months, weeks, and years. Since they all convert to numbers of seconds, you can even add them together like 1.week + 3.days.
* This doesn’t quite stand for Asynchronous JavaScript and XML. The origins of the term Ajax are now a part of computing mythology, but it is not an acronym.
* This will happen if someone’s using your application with JavaScript turned off.
* You can even add your web interface actions to the ItemController class. Then a single controller will implement both the traditional web interface and the web service interface. But you can’t define a web application action with the same name as a web service action, because a controller class can contain only one method with a given name.
—-
by O’Reilly Media
In this fifth article of a six-part series covering web development and Ruby on Rails, you’ll learn how to create an AJAX form and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
15.16 Generating Forms for Manipulating Model Objects
Problem
You want to define actions that let a user create or edit objects stored in the database.
Solution
Let’s create a simple model, and then build forms for it. Here’s some MySQL code to create a table of key-value pairs:
use mywebapp_development;
DROP TABLE IF EXISTS items;
CREATE TABLE `items` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL default ”,
`value` varchar(40) NOT NULL default ‘[empty]‘,
PRIMARY KEY (`id`)
);
Now, from the command line, create the model class, along with a controller and views:
$ ./script/generate model Item
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/item.rb
create test/unit/item_test.rb
create test/fixtures/items.yml
create db/migrate
create db/migrate/ 001_create_items.rb
$ ./script/generate controller items new create edit
exists app/controllers/
exists app/helpers/
create app/views/items
exists test/functional/
create app/controllers/ items_controller.rb
create test/functional/ items_controller_test.rb
create app/helpers/items_helper.rb
create app/views/items/new.rhtml
create app/views/items/edit.rhtml
The first step is to customize a view. Let’s start with app/views/items/new.rhtml. Edit it to look like this:
<!– app/views/items/new.rhtml –>
<%= form_tag :action => “create” %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
All these method calls generate HTML: form_tag opens a <FORM> tag, submit_tag generates a submit button, and so on. You can type out the same HTML by hand and Rails won’t care, but it’s easier to make method calls, and it makes your templates neater.
The text_field call is a little more involved. It creates an <INPUT> tag that shows up in the HTML form as a text entry field. But it also binds the value of that field to one of the members of the @item instance variable. This code creates a text entry field that’s bound to the name member of @item:
<%= text_field “item”, “name” %>
But what’s the @item instance variable? Well, it’s not defined yet, because we’re still using the generated controller. If you try to access the page /items/new page right now, you may get an error complaining about an unexpected nil value. The nil value is the @item variable, which gets used (in text_field calls) without ever being defined.
Let’s customize the ItemsController class so that the new action sets the @item instance variable properly. We’ll also implement the create action so that something actually happens when the user hits the submit button on our generated form.
class ItemsController < ApplicationController
def new
@item = Item.new
end
def create
@item = Item.create(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
Now if you access the /items/new page, you’ll see what you’d expect: a form with two text entry fields. The “Name” field will be blank, and the “Value” field will contain the default database value of “[empty]“.
Fill out the form and submit, and a new row will be created in the items table. You’ll be redirected to the edit action, which doesn’t exist yet. Let’s create it now. Here’s the controller part (note the similarity between ItemsController#edit and ItemsController#create above):
class ItemsController < ApplicationController
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end
In fact, the edit action is so similar to the create action that its form can be almost identical. The only differences are in the arguments to form_tag:
<!– app/views/items/edit.rhtml –>
<%= form_tag :action => “edit”, :id => @item.id %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
Discussion
This is probably the most common day-to-day task faced by web developers. It’s so common that Rails comes with a tool called scaffold that generates this kind of code for you. If you’d invoked generate this way instead of with the arguments given above, Rails would have generate code for the actions given in the Solution, plus a few more:
$ ./script/generate scaffold Items
Starting off with scaffolding doesn’t mean you can get away with not knowing how Rails form generation works, because you’ll definitely want to customize the scaffolding code.
There are two places in our code where magic happens. The first is the text_field call in the view, which is explained in the Solution. It binds a member of an object (@item.name , for instance) to an HTML form control. If you view the source of the /items/new page, you will see that the form fields look something like this:
Name: <input type=”text” name=”item[name]“ value=”" /><br />
Value <input type=”text” name=”item[value]“ value=”[empty]” /><br />
These special field names are used by the second piece of magic, located in the calls to Item.create (in new) and Item#update_attributes. In both cases, an Item object is fed a hash of new values for its members. This hash is embedded into the params hash, which contains CGI form values.
The names of the HTML form fields (item[name] and item[value]) translate into a params hash that looks like this:
{
:item => {
:name => “Name of the item”,
:value => “Value of the item”
},
:controller => “items”,
:action => “create”
}
So this line of code:
Item.create(params[:item])
is effectively the same as this line:
Item.create(:name => “Name of the item”, :value => “Value of the item”)
The call to Item#update_attributes in the edit action works exactly the same way.
As mentioned above, the views for edit and new are very similar, differing only in the destination of the form. With some minor refactoring, we can remove one of the view files completely.
A call to <%= form_tag %> without any parameters at all sets the form destination to the current URL. Let’s change the new.rhtml file appropriately:
<!– app/views/items/new.rhtml –>
<%= form_tag %>
Name: <%= text_field “item”, “name”
%>

Value: <%= text_field “item”, “value”
%>

<%= submit_tag %>
<%= end_form_tag %>
Now the new.rhtml view is suitable for use by both new and edit. We just need to change the new action to call the create method (since the form doesn’t go there anymore), and change the edit action to render new.rhtml instead of edit.rhtml (which can be removed):
class ItemsController < ApplicationController
def new
@item = Item.new
create if request.post?
end
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id and return
end
render :action => ‘new’
end
end
Remember from Recipe 15.5 that a render call only specifies the template file to be used. The render call in edit won’t actually call the new method, so we don’t need to worry about the new method overwriting our value of @item.
In real life, there would be enough differences in the content surrounding the add and edit forms to a separate view for each action. However, there’s usually enough similarity between the forms themselves that they can be refactored into a single partial view (see Recipe 15.14) which both views share. This is a great example of the DRY (Don’t Repeat Yourself) principle. If there is a single form for both the add and edit views, it’s easier and less error-prone to maintain that form as the database schema changes.
See Also
15.17 Creating an Ajax Form
Problem
You want to build a web application that’s responsive and easy to use. You don’t want your users to spend lots of time waiting around for the browser to redraw the screen.
Solution
You can use JavaScript to make the browser’s XMLHTTPRequest object send data to the server, without dragging the user through the familiar (but slow) page refresh. This technique is called Ajax,* and Rails makes it easy to use Ajax without writing or knowing any JavaScript.
Before you can do Ajax in your web application, you must edit your application’s main layout template so that it calls the javascript_include_tag method within its <HEAD> tag. This is the same change made in Recipe 15.15:
<!– app/views/layouts/application.rhtml –>
<html>
<head>
<title>My Web App</title>
<%= javascript_include_tag “prototype”, “effects” %>
</head>
<body>
<%= @content_for_layout %>
</body>
</html>
Let’s change the application from Recipe 15.16 so that the new action is AJAX-enabled (if you followed that recipe all the way through, and made the edit action use new.rhtml instead of edit.rhtml, you’ll need to undo that change and make edit use its own view template).
We’ll start with the view template. Edit app/views/items/new.rhtml to look like this:
<!– app/views/items/new.rhtml –>
<div id=”show_item”></div>
<%= form_remote_tag :url => { :action => :create },
:update => “show_item”,
:complete => visual_effect(:highlight, “show_item”) %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
Those small changes make a standard HTML form into an Ajax form. The main difference is that we call form_remote_tag instead of form_tag. The other differences are the arguments we pass into that method.
The first change is that we put the :action parameter inside a hash passed into the :url option. Ajax forms have more options associated with them than a normal form, so you can’t describe its form action as simply as you can with form_tag.
When the user clicks the submit button, the form values are serialized and sent to the destination action (in this case, create) in the background. The create action processes the form submission as before, and returns a snippet of HTML.
What happens to this HTML? That’s what the :update option is for. It tells Rails to take the result of the form submission, and stick it into the element with the HTML ID of “show_item”. This is why we added that <div id=”show_item”> tag to the top of the template: that’s where the response from the server goes.
The last change to the new.rhtml view is the :complete option. This is a callback argument: it lets you specify a string of JavaScript code that will be run once an Ajax request is complete. We use it to highlight the response from the server once it shows up.
That’s the view. We also need to modify the create action in the controller so that when you make an Ajax form submission, the server returns a snippet of HTML. This is the snippet that’s inserted into the “show_item” element on the browser side. If you make a regular (nonAjax) form submission, the server can behave as it does in Recipe 15.16, and send an HTTP redirect.* Here’s what the controller class needs to look like:
class ItemsController < ApplicationController
def new
@item = Item.new
end
def create
@item = Item.create(params[:item])
if request.xml_http_request?
render :action => ’show’, :layout => false
else
redirect_to :action => ‘edit’, :id => @item.id
end
end
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end
This code references a new view, show. It’s the tiny HTML snippet that’s returned by the server, and stuck into the “show_element” tag by the web browser. We need to define it:
<!– app/views/items/show.rhtml –>
Your most recently created item:<br />
Name: <%= @item.name %><br />
Value: <%= @item.value %><br />
<hr>
Now when you use http://localhost:3000/items/new to add new items to the data base, you won’t be redirected to the edit action. You’ll stay on the new page, and the results of your form submission will be displayed above the form. This makes it easy to create many new items at once.
Discussion
Recipe 15.16 shows how to submit data to a form in the traditional way: the user clicks a “submit” button, the browser sends a request to the server, the server returns a response page, and the browser renders the response page.
Recently, sites like Gmail and Google Maps have popularized techniques for sending and receiving data without a page refresh. Collectively, these techniques are called Ajax. Ajax is a very useful tool for improving your application’s response time and usability.
An Ajax request is a real HTTP request to one of your application’s actions, and you can deal with it as you would any other request. Most of the time, though, you won’t be returning a full HTML page. You’ll just be returning a snippet of data. The web browser will be sending the Ajax request in the context of a full web page (which you served up earlier) that knows how to handle the response snippet.
You can define JavaScript callbacks at several points throughout the lifecycle of an Ajax request. One callback, :complete, was used above to highlight the snippet after inserting it into the page. This table lists the other callbacks.
| Callback name | Callback description |
| :loading | Called when the web browser begins to load the remote document. |
| :loaded | Called when the browser has finished loading the remote document. |
| :interactive | Called when the user can interact with the remote document, even if it has not finished loading. |
| :success | Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range. |
| :failure |
Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range. |
| :complete |
Called when the XMLHttpRequest is complete. If :success and/or :failure are also present, runs after they do. |
15.18 Exposing Web Services on Your Web Site
Problem
You want to offer SOAP and XML-RPC web services from your web application.
Solution
Rails comes with a built-in web service generator that makes it easy to expose a controllers actions as web services. You don’t have to spend time writing WSDL files or even really know how SOAP and XML-RPC work.
Here’s a simple example. First, follow the directions in Recipe 15.16 to create a database table named items, and to generate a model for that table. Don’t generate a controller.
Now, run this from the command line:
./script/generate web_service Item add edit fetch
create app/apis/
exists app/controllers/
exists test/functional/
create app/apis/item_api.rb
create app/controllers/ item_controller.rb
create test/functional/ item_api_test.rb
This creates an item controller that supports three actions: add, edit, and fetch. But instead of web application actions with .rhtml views, these are web service actions that you access with SOAP or XML-RPC.
A Ruby method doesn’t care about the data types of the objects it accepts as arguments, or the data type of its return value. But a SOAP or XML-RPC web service method does care. To expose a Ruby method through a SOAP or XML-RPC interface, we need to define type information for its signature. Open up the file app/apis/ item_api.rb and edit it to look like this:
class ItemApi < ActionWebService::API::Base
api_method :add, :expects => [:string, :string], :returns => [:int]
api_method :edit, :expects => [:int, :string, :string], :returns => [:bool]
api_method :fetch, :expects => [:int], :returns => [Item]
end
Now we need to implement the actual web service interface. Open app/controllers/ item_controller.rb and edit it to look like this:
class ItemController < ApplicationController
wsdl_service_name ‘Item’
def add(name, value)
Item.create(:name => name, :value => value).id
end
def edit(id, name, value)
Item.find(id).update_attributes(:name => name, :value => value)
end
def fetch(id)
Item.find(id)
end
end
Discussion
The item controller now implements SOAP and XML-RPC web services for the items table. This controller can live alongside an items controller that implements a traditional web interface.*
The URL to the XML-RPC API is http://www.yourserver.com/item/api, and the URL to the SOAP API is http://www.yourserver.com/item/service.wsdl. To test these services, here’s a short Ruby script that calls the web service methods through a SOAP client:
require ’soap/wsdlDriver’
wsdl = http://localhost:3000/item/service.wsdl
item_server = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
item_id = item_server.add(’foo’, ‘bar’)
if item_server.edit(item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
else
puts ‘Back to the drawing board…’
end
# Hey, it worked!
item = item_server.fetch(item_id)
item.class # => SOAP::Mapping::Object
item.name # => “John”
item.value # => “Doe”
Here’s the XML-RPC equivalent:
require ‘xmlrpc/client’
item_server = XMLRPC::Client.new2(’http://localhost:3000/item/api’)
item_id = item_server.call(’Add’, ‘foo’, “bar”)
if item_server.call(’Edit’, item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
else
puts ‘Back to the drawing board…’
end
# Hey, it worked!
item = item_server.call(’Fetch’, item_id)
# => {”name”=>”John”, “id”=>2, “value”=>”Doe”}
item.class # => Hash
See Also
15.19 Sending Mail with Rails
Problem
You want to send an email from within your Rails application: perhaps a confirmation of an order, or notification that some action has been taken on a user’s behalf.
Solution
The first is to generate some mailer infrastructure. Go to the application’s base directory and type this command:
./script/generate mailer Notification welcome
exists app/models/
create app/views/notification
exists test/unit/
create test/fixtures/notification
create app/models/notification.rb
create test/unit/ notification_test.rb
create app/views/notification/ welcome.rhtml
create test/fixtures/notification/ welcome
We’re giving the name “Notification” to the mailing center of the application; it’s some-what analogous to a controller in the web interface. The mailer is set up to generate a single email, called “welcome”: this is analagous to an action with a view template.
Now open app/models/notification.rb and edit it to look like this:
class Notification < ActionMailer::Base
def welcome(user, sent_at=Time.now)
@subject = ‘A Friendly Welcome’
@recipients = user.email
@from = ‘admin@mysite.com’
@sent_on = sent_at
@body = {
:user => user,
:sent_on => sent_at
}
attachment ‘text/plain’ do |a|
a.body = File.read(’rules.txt’)
end
end
end
The subject of the email is “A Friendly Welcome”, and it’s sent to the user’s email address from the address “admin@mysite.com”. It’s got an attachment taken from the disk file rules.txt (relative to the root directory of your Rails application).
Although the file notification.rb is within the models/ directory, it acts like a controller in that each of its email messages has an associated view template. The view for the welcome email is in app/views/notification/welcome.rhtml, and it acts almost the same as the view of a normal controller.
The most important difference is that mailer views do not have access to the instance variables of the mailer. To set instance variables for mailers, you pass a hash of those variables to the body method. The keys become instance variable names and the values become their values. In notification.rb , we make two instance variables available to the welcome view, @user and @sent_on. Here’s the view itself:
<!– app/views/notification/welcome.rhtml –>
Hello, <%= @user.name %>, and thanks for signing up at <%= @sent_on
%>. Please print out the attached set of rules and keep them in a
prominent place; they help keep our community running smoothly. Be
sure to pay special attention to sections II.4 (”Assignment of
Intellectual Property Rights”) and XIV.21.a (”Dispute Resolution
Through Ritual Combat”).
To send the welcome email from your Rails application, add the following code to either a controller, a model, or an observer:
Notification.deliver_welcome(user)
Here, the user variable can be any object that implements #name and #email, the two methods called in the welcome method and in the template.
Discussion
You never call the Notification#welcome method directly. In fact, Notification#welcome is not even available, since it’s an instance method, and you never instantiate a Notification object directly. The ActionMailer::Base class defines a method_missing implementation that looks at all calls to undefined class methods. This is why you call deliver_welcome even though you never defined it.
The welcome.rhtml template given above generates plaintext email. To send HTML emails, simply add the following code to Notification#welcome:
content_type ‘text/html’
Now your templates can generate HTML; email clients will recognize the format of the email and render it appropriately.
Sometimes you’ll want more control over the delivery process–for example, when you’re unit-testing your ActionMailer classes. Instead of calling deliver_welcome to send out an email, you can call create_welcome to get the email as a Ruby object. These “create” methods return TMail objects, which you can examine or manipulate as necessary.
If your local web server is incapable of sending email, you can modify environment.rb to contact a remote SMTP server:
Rails::Initializer.run do |config|
config.action_mailer.server_settings = {
:address => ’someserver.com’,
:user_name => ‘uname’,
:password => ‘passwd’,
:authentication => ‘cram_md5′
}
end
See Also
Please check back tomorrow for the conclusion to this article.
—
by O’Reilly Media
17 Oct
Posted by ProCOM
on October 17, 2007 – 12:50 pm - 916 views
In this fourth article of a six-part series covering web development and Ruby on Rails, you’ll learn how to extract code into helper functions, add DHTML effects, and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
15.12 Setting and Retrieving Cookies
Problem
You want to set a cookie from within Rails.
Solution
Recall from Recipe 15.11 that all Rails controllers, views, helpers, and mailers have access to a method called sessions that returns a hash of the current client’s session information. Your controllers, helpers, and mailers (but not your views) also have access to a method called cookies, which returns a hash of the current clients HTTP cookies.
To set a cookie for a user, simply set a key/value pair in that hash. For example, to keep track of how many pages a visitor has looked at, you might set a “visits” cookie:
class ApplicationController < ActionController::Base
before_filter :count_visits
private
def count_visits
value = (cookies[:visits] || ‘0′).to_i
cookies[:visits] = (value + 1).to_s
@visits = cookies[:visits]
end
end
The call to before_filter tells Rails to run this method before calling any action method. The private declaration makes sure that Rails doesn’t think the count_visits method is itself an action method that the public can view.
Since cookies are not directly available to views, count_visits makes the value of the :visits cookie available as the instance variable @visits. This variable can be accessed from a view:
<!– index.rhtml –>
You’ve visited this website’s pages <%= @visits %> time(s).
HTTP cookie values can only be strings. Rails can automatically convert some values to strings, but it’s safest to store only string values in cookies. If you need to store objects that can’t easily be converted to and from strings, you should probably store them in the session hash instead.
Discussion
There may be times when you want more control over your cookies. For instance, Rails cookies expire by default when the user closes their browser session. If you want to change the browser expiration time, you can give cookies a hash that contains an :expires key and a time to expire the cookie. The following cookie will expire after one hour:*
cookies[:user_id] = { :value => ‘123′, :expires => Time.now + 1.hour}
Here are some other options for a cookie hash passed into cookies.
The domain to which this cookie applies:
:domain
The URL path to which this cookie applies (by default, the cookie applies to the entire domain: this means that if you host multiple applications on the same domain, their cookies may conflict):
:path
Whether this cookie is secure (secure cookies are only transmitted over HTTPS connections; the default is false):
:secure
Finally, Rails provides a quick and easy way to delete cookies:
cookies.delete :user_id
Of course, every Ruby hash implements a delete method, but the cookies hash is a little different. It includes special code so that not only does calling delete remove a key-value pair from the cookies hash, it removes the corresponding cookie from the user’s browser.
See Also
15.13 Extracting Code into Helper Functions
Problem
Your views are getting cluttered with Ruby code.
Solution
Let’s create a controller with a fairly complex view to see how this can happen:
$ ./scripts/generate controller list index
exists app/controllers/
exists app/helpers/
create app/views/list
exists test/functional/
create app/controllers/ list_controller.rb
create test/functional/ list_controller_test.rb
create app/helpers/list_helper.rb
create app/views/list/index.rhtml
Edit app/controllers/list_controller.rb to look like this:
class ListController < ApplicationController
def index
@list = [1, "string", :symbol, ['list']]
end
end
Edit app/views/list/index.rhtml to contain the following code. It iterates over each element in @list, and prints out its index and the SHA1 hash of its object ID:
<!– app/views/list/index.rhtml –>
<ul>
<% @list.each_with_index do |item, i| %>
<li class=”<%= i%2==0 ? ‘even’ : ‘odd’ %>”><%= i %>:
<%= SHA1.new(item.id.to_s) %></li>
<% end %>
</ul>
This is pretty messy, but if you’ve done much web programming it should also look sadly familiar.
To clean up this code, we’re going to move some of it into the helper for the controller. In this case, the controller is called list, so its helper lives in app/helpers/list_helper.rb.
Let’s create a helper function called create_li. Given an object and its position in the list, this function creates an <LI> tag suitable for use in the index view:
module ListHelper
def create_li(item, i)
%{<li class=”#{ i%2==0 ? ‘even’ : ‘odd’ }”>#{i}:
#{SHA1.new(item.id.to_s)}</li>}
end
end
The list controller’s views have access to all the functions defined in ListHelper. We can clean up the index view like so:
<!– app/views/list/index.rhtml –>
<ul>
<% @list.each_with_index do |item, i| %>
<%= create_li(item, i) %>
<% end %>
</ul>
Your helper functions can do anything you can normally do from within a view, so they are a great way to abstract out the heavy lifting.
Discussion
The purpose of helper functions is to create more maintainable code, and to enforce a good division of labor between the programmers and the UI designers. Maintainable code is easier for the programmers to work on, and when it’s in helper functions it’s out of the way of the designers, who can tweak the HTML here and there without having to sifting through code.
A good rule of thumb for when to use helpers is to read the code aloud. If it sounds like nonsense to someone familiar with HTML, or it makes up more than a short English sentence, hide it in a helper.
The flip side of this is that you should minimize the amount of HTML generated from within the helpers. That way the UI designers, or other people familiar with HTML, won’t wander your code, wondering where to find the bit of HTML that needs tweaking.
Although helper functions are useful and used very often, Rails also provides partials, another way of extracting code into smaller chunks.
See Also
15.14 Refactoring the View into Partial Snippets of Views
Problem
Your view doesn’t contain a lot of Ruby code, but it’s still becoming more complicated than you’d like. You’d like to refactor the view logic into separate, reusable templates.
Solution
You can refactor a view template into multiple templates called partials. One template can include another by calling the render method, first seen in Recipe 15.5.
Let’s start with a more complex version of the view shown in Recipe 15.5:
<!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>
<ul>
<% @list.each do |item| %>
<li><%= item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => item.id},
:post => true %>
</li>
<% end %>
</ul>
<h2>Add a new item</h2>
<%= form_tag :action => ‘new’ %>
Item: <%= text_field “product”, “name”
%>

<%= submit_tag “Add new item” %>
<%= end_form_tag %>
Here’s the corresponding controller class, and a dummy ListItem class to serve as the model:
# app/controllers/list_controller.rb
class ListController < ActionController::Base
def shopping_list
@list = [ListItem.new(4, 'aspirin'), ListItem.new(199, 'succotash')]
end
# Other actions go here: add, delete, etc.
# …
end
class ListItem
def initialize(id, name)
@id, @name = id, name
end
end
The view has two parts: the first part lists all the items, and the second part prints a form to add a new item. An obvious first step is to split out the new item form.
We can do this by creating a partial view to print the new item form. To do this, create a new file within app/views/list/ called _new_item_form.rhtml. The underscore in front of the filename indicates that it is a partial view, not a full-fledged view for an action called new_item_form. Here’s the partial file.
<!– app/views/list/_new_item_form.rhtml –>
<%= form_tag :action => ‘new’ %>
Item: <%= text_field “item”, “value” %>

<%= submit_tag “Add new item” %>
<%= end_form_tag %>
To include a partial, call the render method from within a template. Here is the _new_ item_form partial integrated into the main view. The view looks exactly the same, but the code is better organized.
<!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>
<ul>
<% @list.each do |item| %>
<li><%= item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => item.id},
:post => true %>
</li>
<% end %>
</ul>
<%= render :partial => ‘new_item_form’ %>
Even though the filename starts with an underscore, when you call the partial, you omit the underscore.
Discussion
Partial views inherit all the instance variables provided by the controller, so they have access to the same instance variables as the parent view. That’s why we didn’t have to change any of the form code for the _new_item_form partial.
We can create a second partial to factor out the code that prints the <LI> tag for each list item. Here’s _list_item.rhtml:
<!– app/views/list/_list_item.rhtml –>
<li><%= list_item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => list_item.id},
:post => true %>
</li>
And heres the revised main view:
<!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>
<ul>
<% @list.each do |item| %>
<%= render :partial => ‘list_item’, :locals => {:list_item => item} %>
<% end %>
</ul>
<%= render :partial => ‘new_item_form’ %>
Partial views do not inherit local variables from their parent view, so the item variable needs to be passed in to the partial, in a special hash called :locals. It’s accessible in the partial as list_item, because that’s the name it was given in the hash.
This scenario, iterating over an Enumerable and rendering a partial for each element, is very common in web applications, so Rails provides a shortcut. We can simplify our main view even more by passing our array into render (as the :collection parameter) and having it do the iteration for us:
<!– app/views/list/shopping_list.rhtml
– >
<h2>My shopping list</h2>
<ul>
<%= render :collection => @list, :partial => ‘list_item’ %>
</ul>
<%= render :partial => ‘new_item_form’ %>
The partial is rendered once for every element in @list. Each list element is made available as the local variable list_item. In case you haven’t guessed, this name comes from the name of the partial itself: render automatically gives _foo.rhtml a local variable called foo.
list_item_counter is another variable that is set automatically (again, the name mirrors the name of the template). list_item_counter is the current item’s index in the collection undergoing iteration. This variable can be handy if you want alternating list items to show up in different styles:
<!– app/views/list/_list_item.rhtml — >
<li><%= list_item.name %> -
<% css_class = list_item_counter % 2 == 0 ? ‘a’ : ‘b’ %>
<%= link_to ‘Delete’, {:action => ‘delete’, :id => list_item.id},
{’class’ => css_class}, :post => true %>
</li>
When there’s no collection present, you can pass a single object into a partial by specifying an :object argument to render. This is simpler than creating a whole hash of :locals just to pass one object. As with :collection, the object will be made available as a local variable whose name is based on the name of the partial.
Here’s an example: we’ll send the shopping list into the new_item_form.rhtml partial, so that the new item form can print a more verbose message. Here’s the change to shopping_list.rhtml:
<%= render :partial => ‘new_item_form’, :object => @list %>
Here’s the new version of _new_item_form.rhtml:
<!– app/views/list/_new_item_form.rhtml –>
<h2>Add a new item to the <%= new_item_form.size %> already in this
list</h2>
<%= form_tag :action => ‘new’ %>
Item: <%= text_field “product”, “name” %>
<%= submit_tag “Add new item” %>
<%= end_form_tag %>
See Also
15.15 Adding DHTML Effects with script.aculo.us
Problem
You want to add fancy effects such as fades to your application, without writing any JavaScript.
Solution
Every Rails application comes bundled with some JavaScript libraries that allow you to create Ajax and DHTML effects. You don’t even have to write JavaScript to enable DHTML in your Rails web site.
First edit your main layout template (see Recipe 15.3) to call javascript_include_tag within your <HEAD> tag:
<!– app/views/layouts/application.rhtml –>
<html>
<head>
<title>My Web App</title>
<%= javascript_include_tag “prototype”, “effects” %>
</head>
<body>
<%= @content_for_layout %>
</body>
</html>
Now within your views you can call the visual_effect method to accomplish the DHTML tricks found in the script.aculo.us library.
Here’s an example of the “highlight” effect:
<p id=”important”>Here is some important text, it will be highlighted
when the page loads.</p>
<script type=”text/javascript”>
<%= visual_effect(:highlight, “important”, :duration => 1.5) %>
</script>
Here’s an example of the “fade” effect:
<p id=”deleted”>Here is some old text, it will fade away when the page
loads.</p>
<script type=”text/javascript”>
<%= visual_effect(:fade, “deleted”, :duration => 1.0) %>
</script>
Discussion
The sample code snippets above are triggered when the page loads, because they’re enclosed in <SCRIPT> tags. In a real application, you’ll probably display text effects in response to user actions: deleted items might fade away, or the selection of one item might highlight related items. Here’s an image that gets squished when you click the link below it:
<img id=”to-squish” src=”bug.jpg”>
<%=link_to_function(”Squish the bug!”, visual_effect(:squish, “to-squish”))%>
The JavaScript code generated by the visual_effect method looks a lot like the arguments you passed into the method. For instance, this piece of a Rails view:
<script type=”text/javascript”>
<%= visual_effect(:fade, ‘deleted-text’, :duration => 1.0) %>
</script>
Generates this JavaScript:
<script type=”text/javascript”>
new Effect.Fade(”deleted-text”, {duration:1.0});
</script>
This similarity means that documentation for the script.aculo.us library is almost directly applicable to visual_effect. It also means that if you feel more comfortable writing straight JavaScript, your code will still be fairly understandable to someone who knows visual_effect.
The following table lists many of the effects available in Rails 1.0.
| JavaScript initialization | Rails initialization |
| new Effect.Highlight |
visual_effect(:highlight) |
| new Effect.Appear | visual_effect(:appear) |
| new Effect.Fade | visual_effect(:fade) |
| new Effect.Puff | visual_effect(:puff) |
| new Effect.BlindDown |
visual_effect(:blind_down) |
| new Effect.BlindUp |
visual_effect(:blind_up) |
| new Effect.SwitchOff |
visual_effect(:switch_off) |
| new Effect.SlideDown |
visual_effect(:slide_down) |
| new Effect.SlideUp |
visual_effect(:slide_up) |
| new Effect.DropOut |
visual_effect(:drop_out) |
| new Effect.Shake | visual_effect(:shake) |
| new Effect.Pulsate | visual_effect(:pulsate) |
| JavaScript initialization |
Rails initialization |
| new Effect.Squish |
visual_effect(:squish) |
| new Effect.Fold |
visual_effect(:fold) |
| new Effect.Grow |
visual_effect(:grow) |
| new Effect.Shrink |
visual_effect(:shrink) |
| new Effect.ScrollTo |
visual_effect(:scroll_to) |
See Also
Please check back tomorrow for the continuation of this article.
—
by O’Reilly Media
16 Oct
Posted by ProCOM
on October 16, 2007 – 12:40 pm - 1,428 views
In this third article of a six-part series covering web development and Ruby on Rails, you’ll learn how to add some very desirable features to your web application. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
15.8 Creating a Login System
Problem
You want your application to support a login system based on user accounts. Users will log in with a unique username and password, as in most commercial and community web sites.
Solution
Create a users table that contains nonnull username and password fields. The SQL to create this table should look something like this MySQL example:
use mywebapp_development;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(40) NOT NULL,
PRIMARY KEY (`id`)
);
Enter the main directory of the application and generate a User model corresponding to this table:
$ ./script/generate model User
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
Open the generated file app/models/user.rb and edit it to look like this:
class User < ActiveRecord::Base
validates_uniqueness_of :username
validates_confirmation_of :password, :on => :create
validates_length_of :password, :within => 5..40
# If a user matching the credentials is found, returns the User object.
# If no matching user is found, returns nil.
def self.authenticate(user_info)
find_by_username_and_password(user_info[:username],
user_info[:password])
end
end
Now you’ve got a User class that represents a user account, and a way of validating a username and password against the one stored in the database.
Discussion
The simple User model given in the Solution defines a method for doing username/password validation, and some validation rules that impose limitations on the data to be stored in the users table. These validation rules tell User to:
Now let’s create a controller for this model. It’ll have a login action to display the login page, a process_login action to check the username and password, and a logout action to deauthenticate a logged-in session. So that the user accounts will actually do something, well also add a my_account action:
$ ./script/generate controller user login process_login logout my_account
exists app/controllers/
exists app/helpers/
create app/views/user
exists test/functional/
create app/controllers/ user_controller.rb
create test/functional/ user_controller_test.rb
create app/helpers/user_helper.rb
create app/views/user/login.rhtml
create app/views/user/ process_login.rhtml
create app/views/user/logout.rhtml
Edit app/controllers/user_controller.rb to define the three actions:
class UserController < ApplicationController
def login
@user = User.new
@user.username = params[:username]
end
def process_login
if user = User.authenticate(params[:user])
session[:id] = user.id # Remember the user’s id during this session
redirect_to session[:return_to] || ‘/’
else
flash[:error] = ‘Invalid login.’
redirect_to :action => ‘login’, :username => params[:user][:username]
end
end
def logout
reset_session
flash[:message] = ‘Logged out.’
redirect_to :action => ‘login’
end
def my_account
end
end
Now for the views. The process_login and logout actions just redirect to other actions, so we only need views for login and my_account. Here’s a view for login:
<!– app/views/user/login.rhtml –>
<% if @flash[:message] %><div><%= @flash[:message] %></div><% end %>
<% if @flash[:error] %><div><%= @flash[:error] %></div><% end %>
<%= form_tag :action => ‘process_login’
%>
Username: <%= text_field “user”, “username” %>

Password: <%= password_field “user”, “password” %>

<%= submit_tag %>
<%= end_form_tag %>
The @flash instance variable is a hashlike object used to store temporary messages for the user between actions. When the logout action sets flash[:message] and redirects to login, or process_login sets flash[:error] and redirects to login, the results are available to the view of the login action. Then they get cleared out.
Here’s a very simple view for my_account:
<!– app/views/user/my_account.rhtml –>
<h1>Account Info</h1>
<p>Your username is <%= User.find(session[:id]).username %>
Create an entry in the users table, start the server, and you’ll find that you can log in from http://localhost:3000/user/login, and view your account information from http:// localhost:3000/user/my_account.
$ ./script/runner ‘User.create(:username => “johndoe”, \
:password => “changeme”)’
There’s just one missing piece: you can visit the my_account action even if you’re not logged in. We don’t have a way to close off an action to unauthenticated users. Add the following code to your app/controllers/application.rb file:
class ApplicationController < ActionController::Base
before_filter :set_user
protected
def set_user
@user = User.find(session[:id]) if @user.nil? && session[:id]
end
def login_required
return true if @user
access_denied
return false
end
def access_denied
session[:return_to] = request.request_uri
flash[:error] = ‘Oops. You need to login before you can view that page.’
redirect_to :controller => ‘user’, :action => ‘login’
end
end
This code defines two filters, set_user and login_required, which you can apply to actions or controllers. The set_user filter is run on every action (because we pass it into before_filter in ApplicationController, the superclass of all our controllers). The set_user method sets the instance variable @user if the user is logged in. Now information about the logged-in user (if any) is available throughout your application. Action methods and views can use this instance variable like any other. This is useful even for actions that don’t require login: for instance, your main layout view might display the name of the logged-in user (if any) on every page.
You can prohibit unauthenticated users from using a specific action or controller by passing the symbol for the login_required method into before_filter. Here’s how to protect the my_account action defined in app/controllers/user_controller.rb:
class UserController < ApplicationController
before_filter :login_required, :only => :my_account
end
Now if you try to use the my_account action without being logged in, you’ll be redirected to the login page.
See Also
15.9 Storing Hashed User Passwords in the Database
Problem
The database table defined in Recipe 15.8 stores users’ passwords as plain text. This is a bad idea: if someone compromises the database, she will have all of your users’ passwords. It’s best to store a secure hash of the password instead. That way, you don’t have the password (so no one can steal it), but you can verify that a user knows his password.
Solution
Recreate the users table from Recipe 15.8 so that instead of a password field, it has a hashed_password field. Here’s some MySQL code to do that:
use mywebapp_development;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`hashed_password` VARCHAR(40) NOT NULL,
PRIMARY KEY (id)
);
Open the file app/models/user.rb created in Recipe 15.8, and edit it to look like this:
require ’sha1′
class User < ActiveRecord::Base
attr_accessor :password
attr_protected :hashed_password
validates_uniqueness_of :username
validates_confirmation_of :password,
:if => lambda { |user| user.new_record? or not user.password.blank? }
validates_length_of :password, :within => 5..40,
:if => lambda { |user| user.new_record? or not user.password.blank? }
def self.hashed(str)
SHA1.new(str).to_s
end
# If a user matching the credentials is found, returns the User object.
# If no matching user is found, returns nil.
def self.authenticate(user_info)
user = find_by_username(user_info[:username])
if user && user.hashed_password == hashed(user_info[:password])
return user
end
end
private
before_save :update_password
# Updates the hashed_password if a plain password was provided.
def update_password
if not password.blank?
self.hashed_password = self.class.hashed(password)
end
end
end
Once you do this, your application will work as before (though you’ll have to convert any preexisting user accounts to the new password format). You don’t need to modify any of the controller or view code, because the User.authenticate method works the same way it did before. This is one of the benefits of separating business logic from presentation logic.
Discussion
There are now three pieces to our user model. The first is the enhanced validation code. The user model now:
When a new user is created, or when the password is changed, User ensures:
The second section of code defines User class methods as before. We add one new class-level method, hashed, which performs the hashing function on a plaintext password. If we want to change hashing mechanisms in the future, we only have to change this method (and migrate any existing passwords).
The third piece of code in the model is a private instance method, update_password, which synchronizes the plaintext password attribute with the hashed version in the database. The call to before_save sets up this method to be called before a User object is saved to the database. This way you can change a user’s password by setting password to its plaintext value, instead of doing the hash yourself.
See Also
15.10 Escaping HTML and JavaScript for Display
Problem
You want to display data that might contain HTML or JavaScript without making browsers render it as HTML or interpret the JavaScript. This is especially important when displaying data entered by users.
Solution
Pass a string of data into the h() helper function to escape its HTML entities. That is, instead of this:
<%= @data %>
Write this:
<%=h @data %>
The h() helper function converts the following characters into their HTML entity equivalents: ampersand (&), double quote (“), left angle bracket (<), and right angle bracket (>).
Discussion
You won’t find the definition for the h() helper function anywhere in the Rails source code, because it’s a shortcut for ERb’s built-in helper function html_escape().
JavaScript is deployed within HTML tags like <SCRIPT>, so escaping an HTML string will neutralize any JavaScript in the HTML. However, sometimes you need to escape just the JavaScript in a string. Rails adds a helper function called escape_javascript() that you can use. This function doesn’t do much: it just turns line breaks into the string “\n”, and adds backslashes before single and double quotes. This is handy when you want to use arbitrary data in your own JavaScript code:
<!– index.rhtml –>
<script lang=”javascript”>
var text = “<%= escape_javascript @javascript_alert_text %>”;
alert(text);
</script>
See Also
15.11 Setting and Retrieving Session Information
Problem
You want to associate some data with each distinct web client that’s using your application. The data needs to persist across HTTP requests.
Solution
You can use cookies (see Recipe 15.12) but it’s usually simpler to put the data in a user’s session. Every visitor to your Rails site is automatically given a session cookie. Rails keys the value of the cookie to a hash of arbitrary data on the server.
Throughout your entire Rails application, in controllers, views, helpers, and mailers, you can access this hash by calling a method called session. The objects stored in this hash are persisted across requests by the same web browser.
This code in a controller tracks the time of a client’s first visit to your web site:
class IndexController < ApplicationController
def index
session[:first_time] ||= Time.now
end
end
Within your view, you can write the following code to display the time:*
<!– index.rhtml –>
You first visited this site on <%= session[:first_time] %>.
That was <%= time_ago_in_words session[:first_time] %> ago.
Discussion
Cookies and sessions are very similar. They both store persistent data about a visitor to your site. They both let you implement stateful operations on top of HTTP, which has no state of its own. The main difference between cookies and sessions is that with cookies, all the data is stored on your visitors’ computers in little cookie files. With sessions, all the data is stored on the web server. The client only keeps a small session cookie, which contains a unique ID that’s tied to the data on the server. No personal data is ever stored on the visitor’s computer.
There are a number of reasons why you might want to use sessions instead of cookies:
On the other hand, cookies are useful when:
Generally, it’s a better idea to use sessions than to store data in cookies.
You can include model objects in your session: this can save a lot of trouble over retrieving the same objects from the database on every request. However, if you are going to do this, it’s a good idea to list in your application controller all the models you’ll be putting into the session. This reduces the risk that Rails won’t be able to deserialize the objects when retrieving them from the session store.
class ApplicationController < ActionController::Base
model :user, :ticket, :item, :history
end
Then you can put ActiveRecord objects into a session:
class IndexController < ApplicationController
def index
session[:user] ||= User.find(params[:id])
end
end
If your site doesn’t need to store any information in sessions, you can disable the feature by adding the following code to your app/controllers/application.rb file:
class ApplicationController < ActionController::Base
session :off
end
As you may have guessed, you can also use the session method to turn sessions off for a single controller:
class MyController < ApplicationController
session :off
end
You can even bring it down to an action level:
class MyController < ApplicationController
session :off, :only => ['index']
def index
#This action will not have any sessions available to it
end
end
The session interface is intended for data that persists over many actions, possibly over the user’s entire visit to the site. If you just need to pass an object (like a status message) to the next action, it’s simpler to use the flash construct described in Recipe 15.8:
flash[:error] = ‘Invalid login.’
By default, Rails sessions are stored on the server via the PStore mechanism. This mechanism uses Marshal to serialize session data to temporary files. This approach works well for small sites, but if your site will be getting a lot of visitors or you need to run your Rails application concurrently on multiple servers, you should explore some of the alternatives.
The three main alternatives are ActiveRecordStore, DRbStore, and MemCacheStore. ActiveRecordStore keeps session information in a database table: you can set up the table by running rake create_sessions_table on the command line. Both DRbStore and MemCacheStore create an in-memory hash that’s accessible over the network, but they use different libraries.
Ruby comes with a standard library called DRb that allows you to share objects (including hashes) over the network. Ruby also has a binding to the Memcached daemon, which has been used to help scale web sites like Slashdot and LiveJournal. Memcached works like a direct store into RAM, and can be distributed automatically over various computers without any special configuration.
To change the session storing mechanism, edit your config/environment.rb file like this:
Rails::Initializer.run do |config|
config.action_controller.session_store = :active_record_store
end
See Also
Please check back tomorrow for the continuation of this article.
—
by O’Reilly Media
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
| By N2H | |||||