Convert context_controller to use RESTful routes (required renaming contexts_controller -> contexts_controller per rails convention).

This change also changes context detail page URLs from /context/my_context to /contexts/my_context

    Add a database index on the projects and contexts tables, user_d + name, to speed the lookup used in urls
    Brought the URLs within various feeds up-to-date
    


git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@399 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2007-01-14 19:18:07 +00:00
parent 8b62e4c784
commit 3fb15f81a5
30 changed files with 122 additions and 113 deletions

View file

@ -1,4 +1,4 @@
class ContextController < ApplicationController
class ContextsController < ApplicationController
helper :todo
@ -13,9 +13,6 @@ class ContextController < ApplicationController
end
end
# Filter the projects to show just the one passed in the URL
# e.g. <home>/context/<context_name> shows just <context_name>.
#
def show
@page_title = "TRACKS::Context: #{@context.name}"
end
@ -23,7 +20,7 @@ class ContextController < ApplicationController
# Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type: application/xml'
# -u username:password
# -d '<request><context><name>new context_name</name></context></request>'
# http://our.tracks.host/context/create
# http://our.tracks.host/contexts
#
def create
if params[:format] == 'application/xml' && params['exception']
@ -101,19 +98,21 @@ class ContextController < ApplicationController
protected
def check_user_set_context
if params["url_friendly_name"]
@context = @user.contexts.find_by_url_friendly_name(params["url_friendly_name"])
if params['url_friendly_name']
@context = @user.contexts.find_by_url_friendly_name(params['url_friendly_name'])
elsif params['id'] && params['id'] =~ /\d+/
@context = @user.contexts.find(params['id'])
elsif params['id']
@context = @user.contexts.find(params["id"])
@context = @user.contexts.find_by_url_friendly_name(params['id'])
else
redirect_to :action => 'index'
end
if @user == @context.user
if @context && @user == @context.user
return @context
else
@context = nil # Should be nil anyway.
notify :warning, "Item and session user mis-match: #{@context.user_id} and #{@user.id}!"
render_text ""
render :text => ''
end
end
@ -124,7 +123,7 @@ class ContextController < ApplicationController
else
@context = nil
notify :warning, "Project and session user mis-match: #{@context.user_id} and #{@user.id}!"
render_text ""
render :text => ''
end
end
@ -134,7 +133,7 @@ class ContextController < ApplicationController
return item
else
notify :warning, "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
render_text ""
render :text => ''
end
end

View file

@ -129,11 +129,11 @@ module ApplicationHelper
end
def link_to_context(context, descriptor = sanitize(context.name))
link_to( descriptor, { :controller => "context", :action => "show", :url_friendly_name => context.url_friendly_name }, :title => "View context: #{context.name}" )
link_to( descriptor, context_path(context), :title => "View context: #{context.name}" )
end
def link_to_project(project, descriptor = sanitize(project.name))
link_to( descriptor, project_url(project), :title => "View project: #{project.name}" )
link_to( descriptor, project_path(project), :title => "View project: #{project.name}" )
end
def item_link_to_context(item)

View file

@ -1,11 +1,11 @@
module ContextHelper
module ContextsHelper
def get_listing_sortable_options
{
:tag => 'div',
:handle => 'handle',
:complete => visual_effect(:highlight, 'list-contexts'),
:url => {:controller => 'context', :action => 'order'}
:url => order_contexts_path
}
end

View file

@ -19,5 +19,9 @@ class Context < ActiveRecord::Base
def hidden?
self.hide == true
end
def to_param
url_friendly_name
end
end

View file

@ -1,48 +0,0 @@
<% context = context_listing %>
<div id="container_<%= context.id %>" class="list">
<!-- %= error_messages_for 'context' % -->
<div id="context-<%= context.id %>" class="even_row" style="display:'';">
<div class="position">
<span class="handle">DRAG</span>
</div>
<div class="data">
<%= link_to_context( context ) %>
<%= " (" + count_undone_todos(context,"actions") + ")" %>
</div>
<div class="buttons">
<% if context.hide? %>
<span class="grey">HIDDEN</span>
<% else %>
<span class="grey">VISIBLE</span>
<% end %>
<%= link_to_remote( image_tag("blank.png", :title =>"Delete context", :class=>"delete_item"),
:loading => visual_effect(:fade, "container_#{context.id}"),
:url => { :controller => "context", :action => "destroy", :id => context.id },
:confirm => "Are you sure that you want to delete the context \'#{context.name}\'?" ) + " " +
link_to_function( image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"), "Element.toggle('context-#{context.id}');Element.toggle('context-#{context.id}-edit-form'); new Effect.Appear('context-#{context.id}-edit-form'); Form.focusFirstElement('form-context-#{context.id}');" ) %>
</div>
</div><!-- [end:context-context.id] -->
<div id="context-<%= context.id %>-edit-form" class="edit-form" style="display:none;">
<%
form_remote_tag_options = { :url => { :controller => 'context', :action => 'update', :id => context.id },
:html => { :id => "form-context-#{context.id}", :class => "inline-form" },
:complete => visual_effect(:appear, 'container_#{context.id}') }
form_remote_tag form_remote_tag_options do -%>
<table style="table-layout: fixed;" width="450">
<%= render :partial => 'context_form', :object => context %>
<tr>
<td width="150">&nbsp; <input type="hidden" name="wants_render" value="false" /></td>
<td width="300"><input type="submit" value="Update" />&nbsp;<a href="javascript:void(0);" onclick="Element.toggle('context-<%= context.id %>');Element.toggle('context-<%= context.id %>-edit-form');Form.reset('form-context-<%= context.id %>');">Cancel</a></td>
</tr>
</table>
<% end -%>
</div><!-- [end:context-context.id-edit-form] -->
</div><!-- [end:context_context.id] -->
<% if controller.action_name == 'create' %>
<script>
new Effect.Appear('context-<%= context.id %>');
</script>
<% end %>

View file

@ -1,2 +0,0 @@
page.replace_html "container_#{@context.id}", :partial => 'context_listing', :object => @context
page.sortable "list-contexts", get_listing_sortable_options

View file

@ -0,0 +1,46 @@
<% context = context_listing %>
<div id="<%= dom_id(context, "container") %>" class="list">
<div id="<%= dom_id(context) %>" class="even_row" style="display:'';">
<div class="position">
<span class="handle">DRAG</span>
</div>
<div class="data">
<%= link_to_context( context ) %> <%= " (" + count_undone_todos(context,"actions") + ")" %>
</div>
<div class="buttons">
<% if context.hide? %>
<span class="grey">HIDDEN</span>
<% else %>
<span class="grey">VISIBLE</span>
<% end %>
<%= link_to_remote( image_tag("blank.png", :title =>"Delete context", :class=>"delete_item"),
:update => dom_id(context, "container"),
:loading => visual_effect(:fade, dom_id(context, 'container')),
:url => context_path(context),
:method => :delete,
:confirm => "Are you sure that you want to delete the context \'#{context.name}\'?" ) %>
<%= link_to_function( image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"), "Element.toggle('#{dom_id(context)}');Element.toggle('#{dom_id(context, 'edit')}'); new Effect.Appear('#{dom_id(context, 'edit')}'); Form.focusFirstElement('#{dom_id(context, 'edit_form')}');" ) %>
</div>
</div>
<div id="<%= dom_id(context, 'edit') %>" class="edit-form" style="display:none;">
<% form_remote_tag_options = { :url => context_path(context),
:method => :put,
:html => { :id => dom_id(context, 'edit_form'), :class => "inline-form" },
:complete => visual_effect(:appear, dom_id(context, "container")) }
form_remote_tag form_remote_tag_options do -%>
<table style="table-layout: fixed;" width="450">
<%= render :partial => 'context_form', :object => context %>
<tr>
<td width="150">&nbsp; <input type="hidden" name="wants_render" value="false" /></td>
<td width="300"><input type="submit" value="Update" />&nbsp;<a href="javascript:void(0);" onclick="Element.toggle('<%= dom_id(context) %>');Element.toggle('<%= dom_id(context, 'edit') %>');Form.reset('<%= dom_id(context, 'edit_form') %>');">Cancel</a></td>
</tr>
</table>
<% end -%>
</div>
</div>
<% if controller.action_name == 'create' %>
<script>
new Effect.Appear('<%= dom_id(context) %>');
</script>
<% end %>

View file

@ -9,21 +9,17 @@
<div id="input_box">
<a href="javascript:void(0)" onClick="Element.toggle('context_new'); Form.focusFirstElement('context-form');" accesskey="n" title="Create a new context">Create new context &#187;</a>
<div id="context_new" class="context_new" style="display:none">
<!--[form:context]-->
<% form_remote_tag :url => { :action => "create" }, :html=> { :id=>'context-form', :name=>'context', :class => 'inline-form' } do -%>
<% form_remote_tag :url => { :action => "create" }, :html=> { :id => 'context-form', :name => 'context', :class => 'inline-form' } do -%>
<div id="status"><%= error_messages_for('context') %></div>
<%= hidden_field( "context", "id" ) %>
<label for="context_name">Context name</label>
<br />
<%= text_field( "context", "name" ) %>
<br />
<label for="context_name">Context name</label><br />
<%= text_field( "context", "name" ) %><br />
<label for="new_context_on_front">Hide from front page?</label>
<%= check_box( "context", "hide" ) %>
<br />
<%= check_box( "context", "hide" ) %><br />
<input type="submit" value="Add" />
<% end -%>
<!--[eoform:context]-->
</div>
</div>

View file

@ -1,5 +1,5 @@
<div id="display_box">
<%= render :partial => "context/context", :locals => { :context => @context, :collapsible => false } %>
<%= render :partial => "contexts/context", :locals => { :context => @context, :collapsible => false } %>
<%= render :partial => "todo/completed", :locals => { :done => @done, :collapsible => false, :append_descriptor => "in this context (last #{@user.prefs.show_number_completed})" } %>
</div><!-- [end:display_box] -->

View file

@ -0,0 +1,2 @@
page.replace_html dom_id(@context, 'container'), :partial => 'context_listing', :object => @context
page.sortable "list-contexts", get_listing_sortable_options

View file

@ -2,12 +2,12 @@ headers["Content-Type"] = "text/xml; charset=utf-8"
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
xml.channel do
xml.title(@title)
xml.link("http://#{@request.host}:#{@request.port}/contexts")
xml.link(contexts_url)
xml.description(@description)
@contexts.each do |c|
xml.item do
xml.title(c.name)
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :url_friendly_name => c.url_friendly_name))
xml.link(context_url(c))
context_description = ''
context_description += "<p>#{count_undone_todos(c)}. "
context_description += "Context is #{c.hidden? ? 'Hidden' : 'Active'}. "

View file

@ -2,12 +2,12 @@
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
xml.channel do
xml.title(@title)
xml.link("http://#{@request.host}:#{@request.port}/projects")
xml.link(projects_url)
xml.description(@description)
@projects.each do |p|
xml.item do
xml.title(p.name)
xml.link(url_for(:only_path => false, :controller => 'project', :action => 'show', :url_friendly_name => p.url_friendly_name))
xml.link(project_url(p))
project_description = ''
project_description += sanitize(markdown( p.description )) if p.description_present?
project_description += "<p>#{count_undone_todos(p)}. "

View file

@ -6,15 +6,15 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
@todos.each do |i|
xml.item do
xml.title(i.description)
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :url_friendly_name => i.context.url_friendly_name))
xml.link(context_url(i.context))
item_notes = sanitize(markdown( i.notes )) if i.notes?
due = "<div>Due: #{format_date(i.due)}</div>\n" if i.due?
toggle_link = link_to( "mark as done", {:only_path => false, :controller => "todo", :action => "toggle_check", :id => i.id})
done = "<div>#{toggle_link}</div>" unless i.completed?
done = "<div>Completed: #{format_date(i.completed_at)}</div>\n" if i.completed?
context_link = link_to( i.context.name, { :only_path => false, :controller => "context", :action => "show", :url_friendly_name => i.context.url_friendly_name } )
context_link = link_to( i.context.name, context_url(i.context) )
if i.project_id?
project_link = link_to (i.project.name, { :only_path => false, :controller => "project", :action => "show", :url_friendly_name => i.project.url_friendly_name} )
project_link = link_to (i.project.name, project_url(i.project) )
else
project_link = "<em>none</em>"
end

View file

@ -41,7 +41,7 @@
<div id="navcontainer">
<ul id="navlist">
<li><%= navigation_link("Home", {:controller => "todo", :action => "index"}, {:accesskey => "t", :title => "Home"} ) %></li>
<li><%= navigation_link( "Contexts", {:controller => "context", :action => "index"}, {:accesskey=>"c", :title=>"Contexts"} ) %></li>
<li><%= navigation_link( "Contexts", contexts_path, {:accesskey=>"c", :title=>"Contexts"} ) %></li>
<li><%= navigation_link( "Projects", projects_path, {:accesskey=>"p", :title=>"Projects"} ) %></li>
<li><%= navigation_link( "Tickler", {:controller => "todo", :action => "tickler"}, :title => "Tickler" ) %></li>
<li><%= navigation_link( "Done", {:controller => "todo", :action => "completed"}, {:accesskey=>"d", :title=>"Completed"} ) %></li>

View file

@ -13,7 +13,7 @@
:url => { :controller => "note", :action => "delete", :id => note.id },
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" ) + "&nbsp;"%><%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
"Element.toggle('note-#{note.id}'); Element.toggle('note-#{note.id}-edit-form'); Effect.Appear('note-#{note.id}-edit-form'); Form.focusFirstElement('form-note-#{note.id}');" ) + " | " %>
<%= link_to("In: " + note.project.name, {:controller => "project", :action => "show", :url_friendly_name => note.project.url_friendly_name}, :class=>"footer_link" ) %>&nbsp;|&nbsp;
<%= link_to("In: " + note.project.name, project_path(note.project), :class=>"footer_link" ) %>&nbsp;|&nbsp;
Created: <%= format_date(note.created_at) %>
<% if note.updated_at? -%>
&nbsp;|&nbsp;Modified: <%= format_date(note.updated_at) %>

View file

@ -1,2 +1,2 @@
page.replace_html "container_#{@project.id}", :partial => 'project_listing', :object => @project
page.replace_html dom_id(@project, 'container'), :partial => 'project_listing', :object => @project
page.sortable "list-projects", get_listing_sortable_options

View file

@ -11,7 +11,7 @@ if @saved
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created
if should_show_new_item()
if @new_context_created
page.insert_html :top, 'display_box', :partial => 'context/context', :locals => { :context => @item.context, :collapsible => true }
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @item.context, :collapsible => true }
else
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@item.context_id}" if source_view_is(:todo)
page.insert_html :bottom, item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type, :source_view => @source_view }

View file

@ -1,6 +1,6 @@
<div id="display_box">
<%= render :partial => "context/context", :collection => @contexts_to_show,
<%= render :partial => "contexts/context", :collection => @contexts_to_show,
:locals => { :collapsible => true } %>
<% unless @done.nil? -%>
<%= render :partial => "todo/completed",

View file

@ -35,13 +35,8 @@ ActionController::Routing::Routes.draw do |map|
map.connect 'tickler', :controller => 'todo', :action => 'tickler'
# Context Routes
map.connect 'context/create', :controller => 'context', :action => 'create'
map.connect 'context/order', :controller => 'context', :action => 'order'
map.connect 'context/:id', :controller=> 'context', :action => 'show', :requirements => {:id => /\d+/}
map.resources :contexts, :collection => {:order => :post}
map.connect 'context/:context/feed/:action/:login/:token', :controller => 'feed'
map.connect 'context/:url_friendly_name', :controller => 'context', :action => 'show'
map.connect 'contexts', :controller => 'context', :action => 'index'
map.connect 'contexts/feed/:feedtype/:login/:token', :controller => 'feed', :action => 'list_contexts_only'
# Projects Routes

View file

@ -0,0 +1,11 @@
class AddFindByNameIndices < ActiveRecord::Migration
def self.up
add_index :projects, [:user_id, :name]
add_index :contexts, [:user_id, :name]
end
def self.down
remove_index :projects, [:user_id, :name]
remove_index :contexts, [:user_id, :name]
end
end

View file

@ -1,15 +1,15 @@
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/todo_container_controller_test_base'
require 'context_controller'
require 'contexts_controller'
# Re-raise errors caught by the controller.
class ContextController; def rescue_action(e) raise e end; end
class ContextsController; def rescue_action(e) raise e end; end
class ContextControllerTest < TodoContainerControllerTestBase
class ContextsControllerTest < TodoContainerControllerTestBase
fixtures :users, :preferences, :contexts
def setup
perform_setup(Context, ContextController)
perform_setup(Context, ContextsController)
end
def test_contexts_list
@ -24,7 +24,7 @@ class ContextControllerTest < TodoContainerControllerTestBase
def test_create_context_with_ajax_success_rjs
ajax_create '@newcontext'
assert_rjs :insert_html, :bottom, "list-contexts"
assert_rjs :sortable, 'list-contexts', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-contexts'), :url => {:controller => 'context', :action => 'order'} }
assert_rjs :sortable, 'list-contexts', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-contexts'), :url => order_contexts_path }
# not yet sure how to write the following properly...
assert_rjs :call, "Form.reset", "context-form"
assert_rjs :call, "Form.focusFirstElement", "context-form"

View file

@ -20,7 +20,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
def test_show_exposes_deferred_todos
@request.session['user_id'] = users(:admin_user).id
p = projects(:timemachine)
get :show, :url_friendly_name => p.url_friendly_name
get :show, :id => p.to_param
assert_not_nil assigns['deferred']
assert_equal 1, assigns['deferred'].size
@ -28,7 +28,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
t.show_from = 1.days.from_now.utc.to_date
t.save!
get :show, :url_friendly_name => p.url_friendly_name
get :show, :id => p.to_param
assert_equal 2, assigns['deferred'].size
end
@ -39,7 +39,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
def test_create_project_with_ajax_success_rjs
ajax_create 'My New Project'
assert_rjs :insert_html, :bottom, "list-projects"
assert_rjs :sortable, 'list-projects', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-projects'), :url => {:controller => 'project', :action => 'order'} }
assert_rjs :sortable, 'list-projects', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-projects'), :url => order_projects_path }
# not yet sure how to write the following properly...
assert_rjs :call, "Form.reset", "project-form"
assert_rjs :call, "Form.focusFirstElement", "project-form"

View file

@ -1,10 +1,10 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'context_controller'
require 'contexts_controller'
# Re-raise errors caught by the controller.
class ContextController; def rescue_action(e) raise e end; end
class ContextsController; def rescue_action(e) raise e end; end
class ContextControllerXmlApiTest < ActionController::IntegrationTest
class ContextsControllerXmlApiTest < ActionController::IntegrationTest
fixtures :users, :contexts
@@context_name = "@newcontext"
@ -57,14 +57,10 @@ class ContextControllerXmlApiTest < ActionController::IntegrationTest
assert_not_nil context1, "expected context '#{@@context_name}' to be created"
end
def test_fails_with_get_verb
authenticated_get_xml "/context/create", users(:other_user).login, 'sesame', {}
end
private
def authenticated_post_xml_to_context_create(postdata = @@valid_postdata, user = users(:other_user).login, password = 'sesame')
authenticated_post_xml "/context/create", user, password, postdata
authenticated_post_xml "/contexts", user, password, postdata
end
def assert_404_invalid_xml

View file

@ -0,0 +1,6 @@
setup :fixtures => :all
include_partial 'login/login', :username => 'admin', :password => 'abracadabra'
open "/contexts"
click "css=#context_3 .buttons img.delete_item"
assert_confirmation "Are you sure that you want to delete the context 'email'?"
wait_for_element_not_present "context_3"

View file

@ -104,5 +104,9 @@ class ContextTest < Test::Unit::TestCase
assert_equal context.id, found_context.id
end
def test_to_param_returns_url_friendly_name
assert_equal 'agenda', @agenda.to_param
end
end