dragonfly-newlisp / example-site / views / dragonfly_routes.html

<% (display-partial "doctype") %>
<head>
	<% (display-partial "header") %>
</head>

<body>

<div id="wrap">
	<div id="header">
		<% (display-partial "navigation") %>	
		<div class="clear"></div>
	</div>

	<div id="content">
				
		<div class="title nomargin">
			<p><% (title "| Dragonfly web framework") %><p>
		</div>
		
		<h1>Default Routes</h1>
		<p class="extract">
			Dragonfly's routes are written in pure newLISP code and have no arbitrary constraints placed on them. They can be as simple or complex as you need them to be. In fact, Dragonfly's routes are so flexible that the defaults will often be all you'll need.
		</p>
		
		<h2>What is a 'route'?</h2>
		<p>
			A <b>route</b>, in an abstract sense, routes a request to its destination.
		</p>
		<p>
			For example, if a request is made for the following URL:
		</p>
		<p><span class="code">http://rundragonfly.com/welcome</span></p>
		<p>
			Then one of Dragonfly's routes (<span class="code">Route.Static</span>) sends the contents of the <b>welcome.html</b> file to your web browser (after evaluating any newLISP code inside of it).
		</p>
		<p>
			Dragonfly's routes are represented as <a href="http://www.newlisp.org/index.cgi?page=newLISP-FOOP">FOOP objects</a> and use
			the convention of prepending "Route." to their name to avoid namespace conflicts.
		</p>
		<p>
			They are stored in the <span class="code">DF:dragonfly-routes</span> list
			where they are asked, one-by-one, whether they wish to handle the current request. The first route to say "yes" gets run.
		</p>
		<p>
			This document describes Dragonfly's two default routes: <span class="code">Route.Static</span> and <span class="code">Route.Resource</span>. A third route included with Dragonfly (but not built into the core) is <span class="code">Route.CGI</span> and is explained in the next topic: <%(link_to "Creating your own routes" "dragonfly_create_routes")%>.
		</p>
		<h2>Templates with <span class="code" style="font-size:inherit">Routes.Static</span></h2>
		<p><span class="code">Route.Static</span> offers a flexible method for serving template files to allow for a PHP-like development pattern, but without the PHP.
		</p>
		<p>
			It handles requests such as the following:
		</p>
		<ul>
			<li>example-site.com/somefile.html</li>
			<li>example-site.com/foo/</li>
		</ul>
		<p>
			This route is very flexible, and there are only three configuration parameters associated with it in <b>config.lsp</b>:
		</p>
		<h3 class="param">ENABLE_STATIC_TEMPLATES</h3>
		<p>Default value: <span class="code"><b>true</b></span></p>
		<p>
			Setting this value to <span class="code"><b>nil</b></span> will remove <span class="code">Route.Static</span> from Dragonfly's list of routes. If you disable this and you're using Dragonfly with the Apache web server, be sure to also comment out this line in <b>.htaccess</b>:
		<pre class="code">RewriteCond %{REQUEST_FILENAME} \.html$</pre>
		<h3 class="param">STATIC_TRIGGER_EXTENSIONS</h3>
		<p>Default value: <span class="code"><b>'(".html")</b></span></p>
		<p>
			If the request has one of these file extensions (including the dot)
			then the route matches if and only if the requested file exists.
		</p>
		<p>
			If the request does not end in one of these extensions then
			the route will attempt to transform it using the
			<span class="code">STATIC_TRANSFORMATIONS</span> to see if that
			results in a matching file (see below).
		</p>
		<p>
			Any GET parameters appended to the URL will not obscure Dragonfly's ability to detect the file extension.
		</p>
		<p class="config"><b>CONFIGURATION</b><br/>
			If you modify this list and are using the Apache server,
			make sure to keep the <b>.htaccess</b> up-to-date with it.
			See the comment there and in <b>config.lsp</b> for more info.
		</p>
		<h3 class="param">STATIC_TRANSFORMATIONS</h3>
		<p>Default value:</p>
		<pre class="textmate-source"><span class="source source_newlisp">'<span class="entity entity_paren entity_paren_lisp">(</span>
    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">string</span> <span class="entity entity_symbol entity_symbol_lisp">DOCUMENT_ROOT</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> _ <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/index.html<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span><span class="entity entity_paren entity_paren_lisp">)</span>
    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">string</span> <span class="entity entity_symbol entity_symbol_lisp">VIEWS_PATH</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> _<span class="entity entity_paren entity_paren_lisp">)</span>
    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">string</span> <span class="entity entity_symbol entity_symbol_lisp">VIEWS_PATH</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> _ <span class="entity entity_symbol entity_symbol_lisp">VIEW_EXTENSION</span><span class="entity entity_paren entity_paren_lisp">)</span>
<span class="entity entity_paren entity_paren_lisp">)</span></span></pre>
		<p>
			If the request does not have one of the
			<span class="code">STATIC_TRIGGER_EXTENSIONS</span>, then
			<span class="code">Route.Static</span> will attempt to transform
			it using this list of possible transformations, where the path is bound to the <span class="code">_</span> symbol.
		</p>
		<p>
			As an example, say we have a folder called <span class="code">foo</span> in our site's
			root, and inside of it is an <span class="code">index.html</span> file. If the user visits the following URL they will be shown the contents of <span class="code">foo/index.html</span>:
		</p>
		<p>
			<span class="code">http://example-site.com/foo</span>
		</p>
		<p>
			This is because the first transformation resulted in a match:
		</p>
		<pre class="textmate-source"><span class="source source_newlisp"><span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">string</span> <span class="entity entity_symbol entity_symbol_lisp">DOCUMENT_ROOT</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> _ <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/index.html<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span><span class="entity entity_paren entity_paren_lisp">)</span>
<span class="comment comment_line comment_line_semicolon comment_line_semicolon_lisp"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_lisp">;</span>=&gt; "/home/www/example-site.com/foo/index.html"</span></span></pre>
		<p>
			Go ahead and <% (link_to "give it a try!" "foo") %>
		</p>
		<p>
			The page you're currently on was matched by the third transformation:
		</p>
<pre class="textmate-source"><span class="source source_newlisp"><span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">string</span> <span class="entity entity_symbol entity_symbol_lisp">VIEWS_PATH</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>/<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> _ <span class="entity entity_symbol entity_symbol_lisp">VIEW_EXTENSION</span><span class="entity entity_paren entity_paren_lisp">)</span></span></pre>
		<p>
			Note that this technique is very powerful as the transformations can contain arbitrarily complex newLISP expressions, so long as they eventually evaluate to a string.
		</p>
		<a name="restful"></a>
		<h2>RESTful routing with <span class="code" style="font-size:inherit">Route.Resource</span></h2>
		<p class="extract">
			Dragonfly supports the creation of powerful web applications through
			the use of clean RESTful routes.
		</p>
		<p><span class="code">Route.Resource</span> handles URLs that refer to <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">RESTful</a> resources, which are represented as FOOP objects deriving from the <span class="code">Resource</span> context. The configuration parameter <span class="code">RESOURCES_PATH</span> specifies the folder containing the resources as .lsp files, one per resource.</p>
		<p>The URL scheme works in a similar manner to twitter's RESTful API:</p>
		<pre class="code">http://mysite.com/<b>resource</b>[/<b>action</b>][/<b>id</b>][.<b>format</b>][?get params..]</pre>
		
		<p>
			<span class="code"><b>resource</b></span> - maps to a context name in a special way:</p>
		<ol><li><span class="code">Resource.</span> is prepended to the name</li>
			<li>Any underscores are removed</li>
			<li>The name is written in title case</li>
		</ol>
		<p>The resource may only have the letters A-Z (lowercase or uppercase), 0-9, the underscore, and it must begin with a letter. For example:
		</p>
		<p>
			<span class="code">my_resource => Resource.MyResource</span>
		</p>
		<p>
			The name also maps to a real file located in <span class="code">RESOURCES_PATH</span> by appending ".lsp" to the name:
		</p>
		<p>
			<span class="code"> my_resource => load file: RESOURCES_PATH/my_resource.lsp</span>
		</p>
		<p>
			<span class="code"><b>action</b></span> (optional) - specifies the function to be called on the resource. Like resource, <span class="code">action</span> may only contain letters, numbers, and the underscore. If no action is specified, then the resource's <a href="http://www.newlisp.org/downloads/newlisp_manual.html#default_function" target="_blank">default function</a> is called instead.
		</p>
		<p>If an action is specified but it's not defined in the resource, then the resource's <span class="code">catch-all</span> function
			is called, which by default displays an error 500 page.
			<br/>
			Whichever function gets called, the <span class="code">id</span> and <span class="code">format</span> are passed in as parameters, in that order,
			except in the case of the <span class="code">catch-all</span> function, in which case the first parameter is the name of the requested
			action, then followed by the <span class="code">id</span> and <span class="code">format</span>.
		</p>
		<p>
			<span class="code"><b>id</b></span> (optional) - may only contain numbers and can be used to specify a specific object out of a collection.
		</p>
		<p>
			<span class="code"><b>format</b></span> (optional) - may only contain letters and can be used to specify the response format. (i.e. xml, json, etc.)
		</p>
		
		<h3 class="code">Example resource in resources/wings.lsp:</h3>
<pre class="textmate-source"><span class="source source_newlisp"><span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">DF:activate-plugin</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>artfulcode/json<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span><span class="entity entity_paren entity_paren_lisp">)</span>

<span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">new</span> <span class="entity entity_symbol entity_symbol_lisp">Resource</span> '<span class="entity entity_symbol entity_symbol_lisp">Resource</span>.<span class="entity entity_symbol entity_symbol_lisp">Wings</span><span class="entity entity_paren entity_paren_lisp">)</span>
<span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">context</span> '<span class="entity entity_symbol entity_symbol_lisp">Resource</span>.<span class="entity entity_symbol entity_symbol_lisp">Wings</span><span class="entity entity_paren entity_paren_lisp">)</span>

<span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">set</span> '<span class="entity entity_symbol entity_symbol_lisp">my-data</span>
  '<span class="entity entity_paren entity_paren_lisp">((</span><span class="entity entity_name entity_name_function entity_name_function_lisp">wings</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">left</span> <span class="entity entity_symbol entity_symbol_lisp">right</span><span class="entity entity_paren entity_paren_lisp">))</span>
    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">wings-condition</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>good<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>excellent<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span><span class="entity entity_paren entity_paren_lisp">))</span>
    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">wings-opacity</span> <span class="constant constant_numeric constant_numeric_lisp">0.5</span><span class="entity entity_paren entity_paren_lisp">))</span>
<span class="entity entity_paren entity_paren_lisp">)</span>

<span class="entity entity_paren entity_paren_lisp">(</span><span class="meta meta_function meta_function_lisp"><span class="storage storage_type storage_type_function-type storage_type_function-type_lisp">define</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_functiondef entity_name_functiondef_lisp">Resource.Wings:Resource.Wings</span></span> <span class="entity entity_symbol entity_symbol_lisp">id</span> <span class="entity entity_symbol entity_symbol_lisp">response-format</span><span class="entity entity_paren entity_paren_lisp">)</span>
    <span class="comment comment_line comment_line_semicolon comment_line_semicolon_lisp"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_lisp">;</span> defaults to calling show
</span>    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">show</span> <span class="entity entity_symbol entity_symbol_lisp">id</span> <span class="entity entity_symbol entity_symbol_lisp">response-format</span><span class="entity entity_paren entity_paren_lisp">)</span>
<span class="entity entity_paren entity_paren_lisp">)</span>

<span class="entity entity_paren entity_paren_lisp">(</span><span class="meta meta_function meta_function_lisp"><span class="storage storage_type storage_type_function-type storage_type_function-type_lisp">define</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_functiondef entity_name_functiondef_lisp">show</span></span> <span class="entity entity_symbol entity_symbol_lisp">id</span> <span class="entity entity_symbol entity_symbol_lisp">response-format</span><span class="entity entity_paren entity_paren_lisp">)</span>
    <span class="comment comment_line comment_line_semicolon comment_line_semicolon_lisp"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_lisp">;</span> in this situation we can't use newLISP's default
</span>    <span class="comment comment_line comment_line_semicolon comment_line_semicolon_lisp"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_lisp">;</span> parameter values to do this for us.
</span>    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">if-not</span> <span class="entity entity_symbol entity_symbol_lisp">id</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">set</span> '<span class="entity entity_symbol entity_symbol_lisp">id</span> <span class="constant constant_numeric constant_numeric_lisp">0</span><span class="entity entity_paren entity_paren_lisp">))</span>

    <span class="comment comment_line comment_line_semicolon comment_line_semicolon_lisp"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_lisp">;</span> uh-oh! No range checking on 'resource-id' ...
</span>    <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">if</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">=</span> <span class="entity entity_symbol entity_symbol_lisp">response-format</span> <span class="string string_quoted string_quoted_double string_quoted_double_lisp"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_lisp">"</span>json<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_lisp">"</span></span><span class="entity entity_paren entity_paren_lisp">)</span>
        <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">begin</span>
            <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">Response:content-type</span> <span class="entity entity_symbol entity_symbol_lisp">Response:json-type</span><span class="entity entity_paren entity_paren_lisp">)</span>
            <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">print</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">Json:lisp-&gt;json</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">my-data</span> <span class="entity entity_symbol entity_symbol_lisp">id</span><span class="entity entity_paren entity_paren_lisp">)))</span>
        <span class="entity entity_paren entity_paren_lisp">)</span>
        <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">begin</span>
            <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">Response:content-type</span> <span class="entity entity_symbol entity_symbol_lisp">Response:text-type</span><span class="entity entity_paren entity_paren_lisp">)</span>
            <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">print</span> <span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">my-data</span> <span class="entity entity_symbol entity_symbol_lisp">id</span><span class="entity entity_paren entity_paren_lisp">))</span>
        <span class="entity entity_paren entity_paren_lisp">)</span>
    <span class="entity entity_paren entity_paren_lisp">)</span>
<span class="entity entity_paren entity_paren_lisp">)</span>

<span class="entity entity_paren entity_paren_lisp">(</span><span class="entity entity_name entity_name_function entity_name_function_lisp">context</span> <span class="entity entity_symbol entity_symbol_lisp">MAIN</span><span class="entity entity_paren entity_paren_lisp">)</span></span></pre>
		<p>Try it out:</p>
		<div style="margin-top:10px">
			<div id="wings_resp" style="width:300px; height:40px; border:1px solid gray; padding:4px; overflow:auto">
				&nbsp;
			</div>
			<div id="wings_request" style="color:#055; font-size:10px; margin-top:6px; font-weight:bold;">(request will be displayed here...)</div>
			<form id="wings_form">
				<p>
					<select id="response_format">
					<option value="">format: default</option>
					<option value=".json">format: json</option>
					</select>
					<select id="wings_id">
						<option value="0">ID: 0</option>
						<option value="1">ID: 1</option>
						<option value="2">ID: 2</option>
						<option value="3">ID: 3 (uh oh..)</option>
					</select>
					<input type="button" value="Submit">
				</p>
			</form>
		</div>
		<script type="text/javascript" charset="utf-8">
			var form = $('#wings_form');
			form.find("input[type='button']").click(function() {
				var req_url = '<%=(web-root "wings")%>';
				req_url += '/' + $('#wings_id').val();
				req_url += $('#response_format').val();
				$('#wings_request').text(req_url);
				$('#wings_resp').text('loading...');
				$.ajax({
					url: req_url,
					success: function (data, status) {
						$('#wings_resp').text(data);
					},
					error: function (xmlReq, status, error) {
						$('#wings_resp').text('Error ' + xmlReq.status + '!');
					}
				});
				return false;
			});
		</script>
		
		<h3>
			Q: Where are the HTTP verbs GET/POST/PUT?
		</h3>
		<p>
			Dragonfly's built-in RESTful route doesn't use them.
		</p>
		<p>
			There are two reasons for this decision:
		</p>
		<ol>
			<li>newLISP's built-in server doesn't set <span class="code">REQUEST_METHOD</span> (currently)</li>
			<li>Their use is redundant and can lead to confusion</li>
		</ol>
		<p>
			To elaborate on point #2, let's take a look at this table (taken from the Ruby on Rails <a href="http://guides.rubyonrails.org/routing.html">routing guide</a>):
		</p>
		<p>
			<img src="<%=(web-root "includes/images/rails_rest.png")%>">
		</p>
		<p>It's not at all clear when a verb should be added to the URL or when an HTTP verb needs to be changed.
		</p>
		<p>
			The problem is that two RESTful abstractions are carelessly mixed with each other: HTTP verbs, and verbs in the URL.</p>
		<p>Whether the RESTful verb in <span class="code">GET /photos/1/edit</span> is "GET" or "edit" is left to philosophical inquiry.</p>
		<p>
			Ironically, the "action" column lists quite clearly which verb is being used:
		</p>
		<p>
			<img src="<%=(web-root "includes/images/rails_rest_fix.png")%>">
		</p>
		<p>
			We can now greatly simplify this table:
		</p>
		<br/>
		<table>
			<tr class="header">
				<td width="110">URL</td>
				<td>controller</td>
				<td>used for</td>
			</tr>
			<tr>
				<td>/photos</td>
				<td>Resource.Photos</td>
				<td>display a list of all images</td>
			</tr>
			<tr>
				<td>/photos/new</td>
				<td>Resource.Photos</td>
				<td>return an HTML form for creating a new image</td>
			</tr>
			<tr>
				<td>/photos/create</td>
				<td>Resource.Photos</td>
				<td>create a new image</td>
			</tr>
			<tr>
				<td>/photos/show/1</td>
				<td>Resource.Photos</td>
				<td>display a specific image</td>
			</tr>
			<tr>
				<td>/photos/edit/1</td>
				<td>Resource.Photos</td>
				<td>return an HTML form for editing an image</td>
			</tr>
			<tr>
				<td>/photos/update/1</td>
				<td>Resource.Photos</td>
				<td>update a specific image</td>
			</tr>
			<tr>
				<td>/photos/destroy/1</td>
				<td>Resource.Photos</td>
				<td>delete a specific image</td>
			</tr>
		</table>
		<h3>
			Q: What about nested resources?
		</h3>
		<p>
			<span class="code">Route.Resource</span> is designed to support the creation of clean and efficient APIs (like <a href="http://apiwiki.twitter.com/Twitter-API-Documentation">twitter's</a>). Therefore it does not support nesting because:
		</p>
		<h4>1. Nested resources are often unnecessary</h4>
		<p>
			You <i>could</i> get comment #5 out of thread #6 in forum #3 like this:
		</p>
		<p><span class="code">/forums/3/threads/6/comments/5</span></p>
		<p>
			But that is a convoluted method of going about it. Implementing a <i>generic</i> system to support that is possible, but it would be complex and limiting. It's far simpler, faster, and clearer to do this instead:
		</p>
		<p><span class="code">/comments/5?forum=3&thread=6</span></p>
		<p>
			Or by passing those parameters in via POST so that the URL is simply:
		</p>
		<p><span class="code">/comments/5</span></p>
		<p>
			Or, if you need it, <%(link_to "implement" "dragonfly_create_routes")%> your own specialized route to support such nesting.
		</p>
		<h4>2. Can lead to poor design and confusion</h4>
		<p>
			This is especially true for implementing some sort of API. Consider the following nested URL (taken from the Rails routing guide):
		</p>
		<p><span class="code">/magazines/1/ads/1/edit</span></p>
		<p>
			Ignoring that this request can be rewritten to take a different form (as shown above), consider the situation where both resources <span class="code">magazines</span> and <span class="code">ads</span> take GET parameters and a request such as the following is made:
		</p>
		<p><span class="code">/magazines/1/ads/1/edit?cat=5</span></p>
		<p>
			How can you tell whether <span class="code">cat=5</span> refers to a category of magazines or ads? Of course there's no way to know by looking at the URL, you'd have to check the API. But what if <i>both</i> magazines and ads use <span class="code">cat</span> to refer to their own internal categories? Then you have a real problem.
		</p>
		<p>
			Also, nested URLs, especially long ones, are a convoluted way of getting what you want. Traversing them results in extra levels of indirection that is ultimately unnecessary and can be inefficient:
		</p>
		<p class="quote"><i>Think about it. If you only want to view a specific comment, you shouldn’t have to specify the account, person, and note for the comment in the URL.</i>
			<span>-- <a href="http://weblog.jamisbuck.org/2007/2/5/nesting-resources">Jamis Buck</a></span></p>
		<p>
			Because of these considerations, as well as the complexities of supporting nested resources in a generic fashion, Dragonfly does not encourage this sort of design pattern by supporting it out-of-the-box. However, if you need such behavior, you've got everything you need to <%(link_to "create it" "dragonfly_create_routes")%>. :-)
		</p>
		
		<% (display-partial "continue") %>
		
		<div class="line-dotted"></div>

		<% (benchmark-result) %>
		
		<div class="line-dotted"></div>
		
		
	</div><!-- END CONTENT -->
	
</div><!-- END WRAP -->
<% (display-partial "footer") %>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.