Chapter 8. XML and Multiple Representations: It all looks different now...

image with no caption

You can’t please everyone all of the time. Or can you? So far we’ve looked at how you can use Rails to quickly and easily develop web apps that perfectly fit one set of requirements. But what do you do when other requirements come along? What should you do if some people want basic web pages, others want a Google mashup, and yet more want your app available as an RSS feed? In this chapter you’ll create multiple representations of the same basic data, giving you the maximum flexibility with minimum effort.

Climbing all over the world

Head First Climbers is a web site for mountaineers all over the world. Climbers report back from expeditions to record the locations and times of mountains they have climbed, and also to report dangerous features they’ve discovered, like rock slides and avalanches.

image with no caption

The information is obviously very important for the safety of other climbers, and many climbers use mobile phones and GPS receivers to read and record information straight from the rock face. Used in the right way, the system will save lives and yet—somehow—the web site’s not getting a lot of traffic.

So why isn’t it popular?

The application is very basic. It’s simply a scaffolded version of this data structure:

Incident

mountain

string

latitude

decimal

longitude

decimal

when

datetime

title

string

description

text

image with no caption

As you’ve noticed by now, scaffolding is a great way to start an application, but you’ll almost always need to modify the code to change the generic scaffolding code into something that’s more appropriate for the problems your users are trying to solve.

So what needs to change about this application?

Do this!

Create a scaffolded application that matches this data structure.

The users hate the interface!

It doesn’t take too long to find out why the web site isn’t popular: the user interface.

The system is used to manage spatial data—it records incidents that happen at particular places and times around the world. The location information is recorded using two numbers:

  • The latitude. This is how far North or South the location is.

  • The longitude. This is a measure of how far West or East a location is.

The users can record their data OK: they just read the latitude and longitude from GPS receivers. But they have a lot of trouble reading and interpreting the information from other climbers.

image with no caption

So people can add data to the application, but they can’t understand the data they get from it. That’s cutting the number of visitors, and the fewer visitors there are the less information is getting added... which causes even less people to use the app. It’s a real downward spiral.

Something needs to be done or the web site will lose so much business it has to close down.

Brain Power

Think about the data that the application needs to display. How would you display the information? What would be the best format to make the information easily comprehensible for the climbers who need it?

The data needs to be on a map

The system records geographic data and it should be displayed on a map.

The correct data is being stored, and the basic functions (create, read, update, and delete) are all available. The problem is presentation. The location is stored as two numbers—the latitude and longitude—but that doesn’t mean it has to be displayed that way.

Instead of seeing this...

image with no caption

...climbers need to see something like this:

image with no caption

Now this is obviously going to be a pretty big change to the interface, so the web site guys have decided that rather than change the whole application, they are going to run a small pilot project to create a version of the page that displays an incident and get it to display a map. But they have no idea what to do, and need your help.

What’s the first thing YOU would do?

We need to create a new action

We don’t want to change the existing code—we only want to add to it. Until we are sure that the new interface works, we don’t want to upset any of the existing users. After all, there aren’t that many left...

So we’ll add a new action called show_with_map. At the moment, someone can see one of the incidents using a URL like this:

http://localhost:3000/incidents/1

We’ll create a new version of the page at:

http://localhost:3000/incidents/map/1

This way, the pilot users only need to add /map to get the new version of the page. We’ll use this for the route:

image with no caption

The new action seems to work...

If you now look at the two versions of the incidents page, we see that they both display the correct data. What do you notice?

Do this!

Create the page template and the new controller method now.

image with no caption

Both versions of the incidents page look identical—and that’s a problem.

The new page needs a map... that’s the point!

But of course we don’t want the new version of the page to look the same. We want to add a map.

So how will we do that? There’s no way we’re going to build our own mapping system. Instead we’ll create a mashup. A mashup is a web application that integrates data and services from other places on the web.

Most of the mapping services allow you to embed maps inside your own web application, but we’ll use the one provided by Google. Google Maps give you a lot of flexibility. Not only can you embed a map in a web page, but you can also, without too much work, add your own data onto the map and program how the user interacts with the map and data.

Here’s a high-level view of how it will work:

image with no caption

The map will be displayed at the approximate location of the recorded incident, and a symbol mark the exact point.

The Head First Climbers application will generate the code to call the map, and the data to display on it, but the map itself, and the bulk of the code that allows the user to do things like drag the map or zoom in and out, will come from the Google Maps server. Even though Google will provide the bulk of the code, we still need to provide two things:

  • The HTML and JavaScript to call the map. This will be a little complex, so we will put the HTML and JavaScript we need in a separate partial that we can call from our page template.

  • The data we need to display on the map. To begin with we will use an example data file to make sure the map’s working.

So what will the map code look like?

So what code do we need?

We need to have the following code in a partial called _map.html.erb:

Download It!

There’s not enough space to display all of the partial code here, but you can download the file at http://tinyurl.com/hfrailsmap

image with no caption

So what does this code do? First of all it calls some JavaScript on the Google Maps server that will generate a map on the web page. The map will have all of the basic drag and zoom functions built in.

But the basic Google code doesn’t do everything we need. It doesn’t load and display any of our local data. So the code in the _map.html.erb partial also loads location data from a file, which it uses to move the map to the correct place and display an icon at a given point.

But there’s a little complication with the code...

The code will only work for localhost

Google places a restriction on the use of the code. They insist that you say which host you’re going to use it on. That means before you can use it on www.yourowndomain.com, you need to tell Google about it. In order to make sure that people comply with this condition, the code will only run if you provide it with a Google Maps key. The key is generated for a particular host name, and if you try to embed a Google map into a page coming from anywhere else, the map will refuse to run.

But for now, there’s not a problem. The _map.html.erb partial we’re going to use has the Google Maps key for localhost—so as long as you run the code on your own machine it will be fine. But remember, you’ll need to apply for your own key before running the code anywhere else.

Geek Bits

If you want to embed Google Maps in your own web apps, you need to sign up with Google. To do this, visit the following URL: http://tinyurl.com/mapreg

Now we need the map data

Before we can try out the embedded map, we need to provide it with map data. To begin with we will just use the test.xml test file. This is what it looks like:

Download It!

To save you typing in the long numbers, you can download the test.xml file from http://tinyurl.com/maptest

image with no caption

The mapping data provides the latitude and longitude of the test incident. When the Google map loads, our map partial will pass it the contents of this file and the incident should be displayed and centered.

What do we need to generate?

We’re passing XML data to the map, and the XML data describes the location of a single incident. The location is given by the latitude, the longitude, the title, and the description. We need to generate XML like this for each incident.

So the system will work something like this:

image with no caption

If this is starting to feel familiar, good! The Google Map is actually using Ajax to work. Remember how we used Ajax to download new version of the seat list in the previous chapter? In the same way, the Google Map will request XML data for the location of an incident.

So the next thing is to generate the data. Where will we get the data from?

We’ll generate XML from the model

The data for the generated XML will come from the Incident model. We’ll be using just four of the attributes, the latitude, longitude, title, and description.

image with no caption

But how do we generate the XML? In a way, this is a little like generating a web page. After all, XML and HTML are very similar. And just as web pages contain data from the model, our XML files will also contain data from the model.

So one option would be to create a page template containing XML tags instead of HTML tags:

image with no caption

That way would work, but there’s a better way...

A model object can generate XML

Model objects contain data. XML files contain data. So it kind of makes sense that model objects can generate XML versions of themselves. Each model object has a method to_xml that returns an XML string:

image with no caption

But creating the XML is only half the story. The other half is returning that XML to the browser. We’re not using a page template, so the whole job will have to be handled by the controller rendering the XML...

What will the controller code look like

We can amend the show_with_map method to output the XML:

image with no caption

The render method returns the XML to the browser. We’ve seen the render method before, but this is a slightly different version. Most of the time you use render to generate a web page from a template or partial. But you can also just pass it a string object—and that’s what we’re doing here.

Geek Bits

To make your life simpler, the Rails folks also allow you to pass a parameter to the render method called :xml

render :xml=>@incident

If the render method is passed an object using the :xml parameter, it will call the to_xml method on the object and send that back to the browser. The :xml version of the render command will generate the same content as the render command in our controller, but it will also set the mime-type of the response to text/xml. But for now, we will use the :text version above.

image with no caption

Meanwhile, at 20,000 feet...

image with no caption

Some people on the pilot program have a problem.

The web pages have disappeared! Before the last amendment a URL like:

http://localhost:3000/incidents/map/1

generated a web page. The trouble is, now that URL just returns XML, instead of a nice Google map.

Before your latest changes

image with no caption

After your latest changes

image with no caption

We need to generate XML and HTML

The show_with_map action originally generated a web page with the show_with_map.html.erb page template. But once we added a render call to the controller method, Rails ignored the template and just generated the XML:

image with no caption

Of course, that makes sense, because there’s no way an action can generate XML and HTML at the same time.

But we still need a web page to display the map, and the map still needs XML map data. So what do we do?

We need some way of calling the controller in one way to generate HTML, and calling the controller in another way to generate XML.

image with no caption

Mark: Another action?

Bob: Sure. One to generate XML and another to generate HTML.

Laura: Well that’s not a great idea.

Bob: Why not?

Laura: That would mean duplicating code. Both methods would have code to read an incident object.

Bob: Whatever. It’s only one line.

Laura: Well now it is. But what if we change things in the future?

Mark: You mean like if the model changes?

Laura: Or if it we get the data from somewhere else, like a web service.

Bob: It’s not such a big deal. Let’s worry about the problems we have right now, okay?

Mark: I don’t know. Laura, what would you do?

Laura: Simple. I’d pass a parameter to the action. Tell it what format we want.

Mark: That might work.

Bob: Come on, too much work.

Laura: Less work than creating another action.

Mark: But one thing...

Laura: Yes?

Mark: Doesn’t the URL identify the information we want?

Laura: So?

Mark: Shouldn’t we use the same URL for both formats?

XML and HTML are just representations

Although the HTML and XML look very different, they are really visual representations of the same thing. Both the HTML web page and the XML map data are both describing the same Incident object data. That incident is the core data, and it’s sometimes called the resource.

A resource is the data being presented by the web page. And the web page is called a representation of the resource. Take an Incident object as an example. The Incident object is the resource. The incident web page and the map data XML file are both representations of the resource.

image with no caption

Thinking about the web as a set of resources and representations is part of a design architecture called REST. REST is the architecture of Rails. And the more RESTful your application is, the better it will run on Rails.

But how does this help us? Well, to be strictly RESTful, both the XML data and the web page should have the same URL (Uniform Resource Locator) because they both represent the same resource. Something like this:

http://localhost:3000/incidents/maps/1

But to simplify things, we can compromise the REST design (a little bit) and use these URLs for the two representations:

image with no caption

How should we decide which format to use?

If we add an extra route that includes the format in the path:

image with no caption

we will be able to read the requested format from the XML and then make decisions in the code like this:

image with no caption

But that’s not how most Rails applications choose the format to generate. Instead they call a method called respond_to do and an object called a responder:

image with no caption

This code does more or less the same thing. The format object is a responder. A responder can decide whether or not to run code, dependent upon the format required by the request. So if the user asks for HTML, the code above will run the code passed to format.html. If the user asks for XML, the responder will run the code passed to format.xml.

So why don’t Rails programmers just use an if statement? After all, wouldn’t that be simpler code? Well, the responder has hidden powers. For example, it sets the mime type of the response. The mime type tells the browser what data-type the response is. In general, it is much better practice to use respond_to do to decide what representation format to generate.

How does the map page work?

Let’s take a deeper look at what just happened and how the HTML page is rendered.

  1. The controller spots that an HTML page is needed.

    The browser points to the HTML version of the page. The controller realizes that HTML rather than XML is required, and so calls show_with_map.html.erb. HTML is sent back to the client browser.

    image with no caption
  2. JavaScript requests the Google Map.

    JavaScript within the web page requests map data from the Google Maps server. The Google Maps server returns it.

    image with no caption
  3. JavaScript requests the incident XML.

    JavaScript within the page requests XML for the incident from the controller. It then displays it on the map.

    image with no caption

The code is ready to go live

Our new version of the location page works well, so let’s replace the scaffolded show action with the show_with_map code.

  1. Remove the routes.

    We created custom routes for the test code, so we need to remove them from the routes.rb file:

    image with no caption
    image with no caption
  2. Rename the show_with_map method in the controller.

    show_with_map is going to become our new show method. So delete the existing show method and rename show_with_map to show.

    image with no caption
  3. Then rename the show_with_map.html. erb template.

    That means we need to delete the existing show.html.erb and replace it with the show_with_map.html.erb template.

    image with no caption
image with no caption

Most web sites now provide RSS news feeds to provide easy links to the main resources on a site.

But what does an RSS news feed look like?

RSS feeds are just XML

This is what an RSS feed file would look like for the climbing site:

image with no caption

This is just an XML file. If you use an RSS news reader, or if your browser can subscribe to RSS news feeds, they will download a file just like this, which contains a list of links and descriptions to news stories.

So how can WE generate an RSS feed like this?

Brain Power

Do any of the tags in the RSS look particularly surprising or unclear? What do you think channel does? What about link?

We’ll create an action called news

Let’s create a new route as follows:

map.connect '/incidents/news', :action=>'news', :controller=>'incidents', :format=>'xml'

Brain Power

Is there a problem converting the XML to match the structure of the RSS news feed?

We have to change the structure of the XML

The to_xml method allows us to make a few simple changes to the XML it produces. We can swap names and choose which data items to include. But will it give us enough power to turn the XML we have into the XML we want?

image with no caption

We need more XML POWER

The news feed XML can’t be generated by the to_xml method. While to_xml can modify XML output slightly, it can’t radically change XML structure. For instance, to_xml can’t move elements between levels. It can’t group elements within other elements. to_xml is designed to be quick and easy to use, but that also makes it a bit inflexible.

For true XML power, we need something more...

So we’ll use a new kind of template: an XML builder

If we created another HTML page template, we could generate whatever XML output we like. After all, HTML is similar to XML:

image with no caption

But Rails provides a special type of template that is specifically designed to generate XML; it’s called an XML Builder Template.

XML Builders live in the same directory as page templates, and they are used in a similar way. If someone has requested an XML response (by adding .xml to the end of the URL), the controller only needs to read the data from the model, and Rails will automatically call the XML builder template. That means we can lose a line from the news action:

image with no caption

This code will now just read the data from the model and the XML bulder template will do the rest.

image with no caption

So what does an XML builder look like?

Now let’s add the feed to the pages

But how will users find the feed? Browsers sense the presence of a news feed by looking for a <link... /> reference within a page.

The folks at Head First Climbers want the news feed to appear on every page, so we will add a reference to the RSS feed in the incidents layout file, using the auto_discovery_link helper:

image with no caption

This should create a link like this:

<link href="http://localhost:3000/incidents/news.xml"
  rel="alternate" title="RSS" type="application/rss+xml" />

But to see if it works, we need to fire up our web browser again.

On top of the world!

One of the first news items on the web site is posted by our intrepid climber, and thousands of climbers hear of the good news.

image with no caption

Tools for your Rails Toolbox

You’ve got Chapter 8 under your belt, and now you’ve added the ability to use XML to represent your pages in multiple ways.

Rails Tools

to_xml generates an XML for any model object

:only and :root parameters allow you to modify the to_xml format

respond_to creates a _responder_ object that will help you generate multiple representations for a resource

XML builder templates are like page templates for creating XML

XML builder templates give you more flexibility than by simply using to_xml

responders set the response mime-type and also decide whether to call page templates or XML builder templates

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.236.142.143