Renamed TaskController and TaskControllerTest to TasksController and
TasksControllerTest. Added a new resource to routes.rb to provide
RESTful methods for working with Task objects. Updated all of the unit
and functional tests and renamed and refactored the actions in the
controller.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/backlog_controller.rb | 1 +
app/controllers/task_controller.rb | 127 ------------
app/controllers/tasks_controller.rb | 167 ++++++++++++++++
app/helpers/application_helper.rb | 9 -
app/models/task.rb | 71 +++++--
app/views/backlog/view.html.erb | 66 +------
app/views/task/_edit.html.erb | 47 -----
app/views/task/create.html.erb | 3 -
app/views/task/modify.html.erb | 1 -
app/views/tasks/_edit.html.erb | 66 +++++++
app/views/tasks/_list.html.erb | 34 ++++
app/views/tasks/edit.html.erb | 1 +
app/views/tasks/index.html.erb | 1 +
app/views/tasks/new.html.erb | 3 +
app/views/tasks/show.html.erb | 35 ++++
config/routes.rb | 5 +
public/stylesheets/forms.css | 5 +
test/functional/task_controller_test.rb | 233 ----------------------
test/functional/tasks_controller_test.rb | 313 ++++++++++++++++++++++++++++++
test/unit/product_test.rb | 3 +-
test/unit/task_test.rb | 121 +++++++++++-
21 files changed, 806 insertions(+), 506 deletions(-)
delete mode 100644 app/controllers/task_controller.rb
create mode 100644 app/controllers/tasks_controller.rb
delete mode 100644 app/views/task/_edit.html.erb
delete mode 100644 app/views/task/create.html.erb
delete mode 100644 app/views/task/modify.html.erb
create mode 100644 app/views/tasks/_edit.html.erb
create mode 100644 app/views/tasks/_list.html.erb
create mode 100644 app/views/tasks/edit.html.erb
create mode 100644 app/views/tasks/index.html.erb
create mode 100644 app/views/tasks/new.html.erb
create mode 100644 app/views/tasks/show.html.erb
delete mode 100644 test/functional/task_controller_test.rb
create mode 100644 test/functional/tasks_controller_test.rb
diff --git a/app/controllers/backlog_controller.rb
b/app/controllers/backlog_controller.rb
index 3e6ca56..6011264 100644
--- a/app/controllers/backlog_controller.rb
+++ b/app/controllers/backlog_controller.rb
@@ -28,6 +28,7 @@ class BacklogController < ApplicationController
@task = Task.new
@task.primary = @user if @user
@users = User.find(:all, :conditions => "id in (select user_id from
product_roles where product_id = #{(a)product.id})")
+ @tasks = @backlog_item.tasks
end
def modify
diff --git a/app/controllers/task_controller.rb b/app/controllers/task_controller.rb
deleted file mode 100644
index 6c6e42b..0000000
--- a/app/controllers/task_controller.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-# task_controller.rb
-# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program. If not, see <
http://www.gnu.org/licenses/>.
-#
-
-# +TaskController+ allows the user to create and modify tasks for a backlog
-# item.
-#
-class TaskController < ApplicationController
- before_filter :authenticated, :except => [:view]
- before_filter :load_backlog_item, :only => [:create]
- before_filter :load_task, :only => [:modify, :delete]
- before_filter :authorized_to_create, :only => [:create]
- before_filter :authorized_to_manage, :only => [:modify, :delete]
- before_filter :before_edit, :only => [:create, :modify]
- before_filter :ensure_sprint_is_active, :only => [:create]
-
- def create
- if request.post?
- begin
- Task.transaction do
- @task = Task.create(params[:task])
- @task.backlog_item = @backlog_item
- @backlog_item.tasks << @task
- @backlog_item.save!
- @task.save!
-
- redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
- end
-
- rescue ActiveRecord::RecordInvalid => error
- flash[:message] = error.message
- @task.valid?
- render :action => :create
- end
- else
- @task = Task.new(:backlog_item => @backlog_item, :primary => @user)
- end
- end
-
- def modify
- if request.post?
- @task.update_attributes(params[:task])
-
- @task.save
-
- redirect_to :controller => :backlog, :action => :view, :id =>
@task.backlog_item.id
- end
- end
-
- def delete
- if request.post?
- begin
- Task.transaction do
- @task.destroy
- end
-
- redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
-
- rescue Exception => error
- flash[:message] = error.message
-
- redirect_to error_path
- end
- else
- redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
- end
- end
-
- private
-
- def load_backlog_item
- @backlog_item = BacklogItem.find_by_id(params[:backlog_item])
-
- unless @backlog_item
- flash[:message] = 'Invalid user id.'
- redirect_to error_path
- end
- end
-
- def load_task
- @task = Task.find_by_id(params[:id])
- @backlog_item = @task.backlog_item if @task
-
- unless @task
- flash[:message] = 'Invalid task id.'
- redirect_to error_path
- end
- end
-
- def authorized_to_create
- unless TaskHelper.can_create_task(@user,@backlog_item)
- flash[:message] = 'You are not authorized to work on tasks for this user
story.'
- redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
- end
- end
-
- def authorized_to_manage
- unless TaskHelper.can_manage_task(@user,@task)
- flash[:message] = 'You are not authorized to modify this task.'
- redirect_to :controller => :backlog, :action => :view, :id =>
@task.backlog_item.id
- end
- end
-
- def before_edit
- @users = User.find(:all, :order => 'display_name ASC')
- end
-
- def ensure_sprint_is_active
- unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE
- flash[:message] = 'The sprint must be active for that action.'
- redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
- end
- end
-end
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
new file mode 100644
index 0000000..af3aec0
--- /dev/null
+++ b/app/controllers/tasks_controller.rb
@@ -0,0 +1,167 @@
+# tasks_controller.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+# +TasksController+ allows the user to create and modify tasks for a backlog
+# item.
+#
+class TasksController < ApplicationController
+ before_filter :authenticated, :except => [:index, :show]
+ before_filter :load_backlog_item, :only => [:index, :new, :create]
+ before_filter :load_task, :only => [:show, :edit, :update, :destroy]
+ before_filter :before_edit, :only => [:create, :update]
+ before_filter :ensure_sprint_is_active, :only => [:create]
+
+ # GET /tasks
+ def index
+ @tasks = Task.find_all_by_backlog_item_id((a)backlog_item.id)
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ # GET /tasks/1
+ def show
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ # GET /tasks/new
+ def new
+ if @backlog_item.user_can_add_tasks?(@user)
+ @task = Task.new(
+ :backlog_item_id => @backlog_item.id,
+ :primary_id => @user.id
+ )
+ @users = User.find(:all, :order => 'display_name ASC')
+ else
+ flash[:error] = 'You are not allowed to add tasks to this backlog item.'
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+ end
+
+ # GET /tasks/1;edit
+ def edit
+ unless @task.user_can_edit?(@user)
+ flash[:error] = "You are not allowd to edit this task."
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+ @users = User.find(:all, :order => 'display_name ASC')
+ end
+
+ # POST /tasks
+ def create
+ if @backlog_item.user_can_add_tasks?(@user)
+ Task.transaction do
+ @task = Task.new(params[:task])
+ @task.backlog_item = @backlog_item
+
+ respond_to do |format|
+ if @task.save
+ flash[:message] = "Recorded #{(a)task.hours} against this backlog
item."
+ format.html { redirect_to :controller => :backlog, :action => :view,
:id => @backlog_item }
+ else
+ format.html { render :action => 'new' }
+ end
+ end
+ end
+ else
+ flash[:error] = "You are not allowed to add tasks to this backlog item."
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+ end
+
+ # PUT /tasks/1
+ def update
+ if @task.user_can_edit?(@user)
+ begin
+ Task.transaction do
+ @task.update_attributes(params[:task])
+ @task.save!
+ end
+
+ flash[:message] = "Task updated."
+ rescue Exception => error
+ flash[:error] = "ERROR: #{error.message}"
+ end
+
+ respond_to do |format|
+ format.html { redirect_to :controller => :backlog, :action => :view, :id
=> @backlog_item }
+ end
+ else
+ flash[:error] = "You are not allowed to update that task."
+
+ @task.valid?
+
+ respond_to do |format|
+ format.html { redirect_to :controller => :backlog, :action => :view, :id
=> @backlog_item }
+ end
+ end
+ end
+
+ # DELETE /tasks/1
+ def destroy
+ if @task.user_can_delete?(@user)
+ begin
+ Task.transaction do
+ @task.destroy
+ end
+
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ rescue Exception => error
+ flash[:error] = "ERROR: #{error.message}"
+ redirect_to error_url
+ end
+ else
+ flash[:error] = 'You are not allowed to delete this task.'
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+ end
+
+ private
+
+ def load_backlog_item
+ @backlog_item = BacklogItem.find_by_id(params[:backlog_item])
+
+ unless @backlog_item
+ flash[:message] = 'Invalid user id.'
+ redirect_to error_path
+ end
+ end
+
+ def load_task
+ @task = Task.find_by_id(params[:id])
+ @backlog_item = @task.backlog_item if @task
+
+ unless @task
+ flash[:message] = 'Invalid task id.'
+ redirect_to error_path
+ end
+ end
+
+ def before_edit
+ @users = User.find(:all, :order => 'display_name ASC')
+ end
+
+ def ensure_sprint_is_active
+ unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE
+ flash[:message] = 'The sprint must be active for that action.'
+ redirect_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
+ end
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f20df88..96d59a7 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -102,15 +102,6 @@ module ApplicationHelper
:id => backlog_item.id)
end
- # Creates a link to a task.
- #
- def link_to_task(task, options = {})
- @template.link_to(options[:text] ? options[:text] : task.id,
- :controller => :task,
- :action => options[:action] ? options[:action] : :view,
- :id => task.id)
- end
-
# Shows the hours for a given object as estimated, actual and remaining.
#
def show_hours_as_ear(estimated)
diff --git a/app/models/task.rb b/app/models/task.rb
index 89af73d..7785b4d 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -1,39 +1,74 @@
# task.rb
# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
#
# A +Task+ represents a single piece of work associated with a +UserStory+.
-# Tasks have a primary developer, and an optional backup developer.
+# Tasks have a primary developer, and an optional backup developer.
class Task < ActiveRecord::Base
validates_presence_of :backlog_item_id
belongs_to :primary, :class_name => 'User', :foreign_key =>
'primary_id'
belongs_to :backup, :class_name => 'User', :foreign_key =>
'backup_id'
+ validates_presence_of :primary_id,
+ :message => 'A primary developer must be defined.'
+
validates_presence_of :description,
- :message => 'You need to describe this task.'
+ :message => 'You need to describe this task.'
validates_length_of :description,
- :maximum => 1000,
- :too_long => 'Please shorten the description.'
+ :maximum => 1000,
+ :too_long => 'Please shorten the description.'
- validates_presence_of :hours,
- :message => 'You must report the hours worked on this
task.'
+ validates_presence_of :hours,
+ :message => 'You must report the hours worked on this task.'
validates_numericality_of :hours,
- :greater_than => 0,
- :message => 'Hours must be numeric and greater than
0.'
+ :greater_than => 0,
+ :message => 'Hours must be numeric and greater than 0.'
+
+ validates_presence_of :when,
+ :message => 'You must specify when the task was performed.'
belongs_to :backlog_item
+
+ # Returns whether the user can edit the task.
+ #
+ def user_can_edit?(user)
+ user &&
+ backlog_item.sprint.active? &&
+ (primary && (primary.id == user.id))
+ end
+
+ # Returns whether the user can delete the task or not.
+ #
+ def user_can_delete?(user)
+ user &&
+ (backlog_item.owner && (backlog_item.owner.id == user.id)) &&
+ backlog_item.sprint.active?
+ end
+
+ protected
+
+ def validate
+ if primary && backup && (primary.id == backup.id)
+ errors.add('backup_id',"The backup developer cannot also be
#{primary.display_name}.")
+ end
+ if backlog_item
+ unless (backlog_item.sprint.start...(backlog_item.sprint.start +
backlog_item.sprint.duration)).include? self.when
+ errors.add('when', 'Task must be done during the sprint.')
+ end
+ end
+ end
end
diff --git a/app/views/backlog/view.html.erb b/app/views/backlog/view.html.erb
index c02f27e..8ee0333 100644
--- a/app/views/backlog/view.html.erb
+++ b/app/views/backlog/view.html.erb
@@ -63,67 +63,5 @@
</tbody>
</table>
-<table class="list">
- <colgroup>
- <col class="row_id" />
- <col class="description" />
- <col class="user" />
- <col class="user" />
- <col class="hours" />
- <col class="date" />
- <col class="actions" />
- </colgroup>
-
- <thead>
- <tr>
- <th colspan="7" class="title">Tasks</th>
- </tr>
- <tr>
- <th>##</th>
- <th>Description</th>
- <th>Primary</th>
- <th>Backup</th>
- <th>Hours</th>
- <th>Date</th>
- <th>Action</th>
- </tr>
- </thead>
-
- <tbody>
-
- <% if @backlog_item.user_can_add_tasks?(@user) %>
- <% form_for :task, :url => {:controller => :task, :action => :create}
do |form| %>
- <%= hidden_field_tag :backlog_item, @backlog_item.id %>
- <tr>
- <td></td>
- <td><%= form.text_field :description %></td>
- <td><%= collection_select :task, :primary_id, @users, :id,
- :display_name, {:include_blank => true} %></td>
- <td><%= collection_select :task, :backup_id, @users, :id,
- :display_name, {:include_blank => true} %></td>
- <td><%= form.text_field :hours %></td>
- <td><%= form.date_select :when %></td>
- <td><%= submit_tag "Create" %></td>
- </tr>
- <% end %>
- <% end %>
-
- <% @backlog_item.tasks.each_with_index do |task,i| %>
- <tr class="<% row_class = i%2 == 0 ? 'even' : 'odd'
%>">
-
- <td><%= link_to_task task, :action => :modify %></td>
- <td><%= simple_format task.description %></td>
- <td><%= link_to_user task.primary %></td>
- <td><%= link_to_user task.backup %></td>
- <td><%= task.hours %></td>
- <td><%= task.when.to_s(:date) %></td>
- <td>
- <% form_for :task, :url => {:controller => :task, :action =>
:delete} do |f| %>
- <%= hidden_field_tag :id, task.id %>
- <%= submit_tag "Delete" %>
- <% end %>
- </td>
- </tr>
- <% end %>
- </tbody>
-</table>
+<%= link_to "Add Task", new_task_path(:backlog_item => @backlog_item)
%>
+<%= render(:partial => 'tasks/list', :object => @backlog_item.tasks)
%>
diff --git a/app/views/task/_edit.html.erb b/app/views/task/_edit.html.erb
deleted file mode 100644
index 4195c45..0000000
--- a/app/views/task/_edit.html.erb
+++ /dev/null
@@ -1,47 +0,0 @@
-<div>
- <% form_for :task do |form| %>
-
- <%= hidden_field_tag :backlog_item, @task.backlog_item.id %>
-
- <table class="edit">
- <tbody>
- <tr>
- <td class="label">Primary:</td>
- <td class="value">
- <%= collection_select :task, :primary_id, @users, :id,
- :display_name, {:include_blank => true} %>
- </td>
- </tr>
-
- <tr>
- <td class="label">Backup:</td>
- <td class="value">
- <%= collection_select :task, :backup_id, @users, :id,
- :display_name, {:include_blank => true} %>
- </td>
- </tr>
-
- <tr>
- <td class="label">Hours:</td>
- <td class="value"><%= form.text_field :hours
%></td>
- </tr>
-
- <tr>
- <td class="label">Description:</td>
- <td class="value"><%= form.text_area :description
%></td>
- </tr>
-
- <tr>
- <td class="buttons" colspan="2">
- <% if @task.new_record? %>
- <%= submit_tag "Create", :class => 'button' %>
- <% else %>
- <%= submit_tag "Update", :class => 'button' %>
- <% end %>
- </td>
- </tr>
- </tbody>
- </table>
-
- <% end %>
-</div>
\ No newline at end of file
diff --git a/app/views/task/create.html.erb b/app/views/task/create.html.erb
deleted file mode 100644
index ad1a69b..0000000
--- a/app/views/task/create.html.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<div>
- <%= render :partial => 'edit', :object => @task %>
-</div>
\ No newline at end of file
diff --git a/app/views/task/modify.html.erb b/app/views/task/modify.html.erb
deleted file mode 100644
index e536180..0000000
--- a/app/views/task/modify.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= render :partial => "edit", :object => @task %>
diff --git a/app/views/tasks/_edit.html.erb b/app/views/tasks/_edit.html.erb
new file mode 100644
index 0000000..5fe80e8
--- /dev/null
+++ b/app/views/tasks/_edit.html.erb
@@ -0,0 +1,66 @@
+<div>
+ <% form_for(:task, @task, :url => tasks_path) do |form| %>
+
+ <%= hidden_field_tag :backlog_item, @task.backlog_item.id %>
+
+ <table class="edit">
+ <tbody>
+ <tr>
+ <td class="label">When</td>
+ <td class="value">
+ <%= form.date_select :when %>
+ <%= error_message_on(:task, :when) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Primary</td>
+ <td class="value">
+ <%= collection_select :task, :primary_id, @users, :id,
+ :display_name, {:include_blank => true} %>
+ <%= error_message_on(:task, :primary_id) %>
+
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Backup</td>
+ <td class="value">
+ <%= collection_select :task, :backup_id, @users, :id,
+ :display_name, {:include_blank => true} %>
+ <%= error_message_on(:task, :backup_id) %>
+
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Hours</td>
+ <td class="value">
+ <%= form.text_field :hours %>
+ <%= error_message_on(:task, :hours) %>
+
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Description</td>
+ <td class="value">
+ <%= form.text_area :description %>
+ <%= error_message_on(:task, :description) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="buttons" colspan="2">
+ <% if @task.new_record? %>
+ <%= submit_tag "Create", :class => 'button' %>
+ <% else %>
+ <%= submit_tag "Update", :class => 'button' %>
+ <% end %>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <% end %>
+</div>
diff --git a/app/views/tasks/_list.html.erb b/app/views/tasks/_list.html.erb
new file mode 100644
index 0000000..273296e
--- /dev/null
+++ b/app/views/tasks/_list.html.erb
@@ -0,0 +1,34 @@
+<table class="list">
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Description</th>
+ <th>Primary</th>
+ <th>Backup</th>
+ <th>Hours</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <% @tasks.each_with_index do |task, index| %>
+ <% row_class = index%2 == 0 ? 'even' : 'odd' %>
+ <tr class="<%= row_class %>">
+ <td><%= task.when %></td>
+ <td><%= simple_format task.description %></td>
+ <td><%= link_to_user task.primary %></td>
+ <td><%= link_to_user task.backup %></td>
+ <td><%= format "%0.2f", task.hours %>
+ <td>
+ <%= link_to "View", task_path(task) %>
+ <% if task.user_can_edit?(@user) %>
+ <%= link_to "Edit", edit_task_path(task) %>
+ <% end %>
+ <% if task.user_can_delete?(@user) %>
+ <%= link_to "Delete", task_path(task), :confirm => "Are
you sure?", :method => :delete %>
+ <% end %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
diff --git a/app/views/tasks/edit.html.erb b/app/views/tasks/edit.html.erb
new file mode 100644
index 0000000..e536180
--- /dev/null
+++ b/app/views/tasks/edit.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "edit", :object => @task %>
diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb
new file mode 100644
index 0000000..0a9c778
--- /dev/null
+++ b/app/views/tasks/index.html.erb
@@ -0,0 +1 @@
+<%= render(:partial => 'list') %>
diff --git a/app/views/tasks/new.html.erb b/app/views/tasks/new.html.erb
new file mode 100644
index 0000000..51d818d
--- /dev/null
+++ b/app/views/tasks/new.html.erb
@@ -0,0 +1,3 @@
+<div>
+ <%= render :partial => 'edit', :object => @task %>
+</div>
diff --git a/app/views/tasks/show.html.erb b/app/views/tasks/show.html.erb
new file mode 100644
index 0000000..fb0cfb9
--- /dev/null
+++ b/app/views/tasks/show.html.erb
@@ -0,0 +1,35 @@
+<table class="details">
+ <tr>
+ <td class="label">Description:</td>
+ <td class="value"><%= simple_format @task.description
%></td>
+ </tr>
+
+ <tr>
+ <td class="label">When:</td>
+ <td class="value"><%= @task.when %></td>
+ </tr>
+
+ <tr>
+ <td class="label">Hours:</td>
+ <td class="value"><%= format "%0.2f", @task.hours
%></td>
+ </tr>
+
+ <tr>
+ <td class="label">Primary:</td>
+ <td class="value"><%= link_to_user @task.primary %></td>
+ </tr>
+
+ <tr>
+ <td class="label">Backup:</td>
+ <td class="label"><%= link_to_user @task.backup %></td>
+ </tr>
+
+</table>
+
+<%= link_to "Back", :controller => :backlog, :action => :view, :id
=> @task.backlog_item %>
+<% if @task.user_can_edit?(@user) %>
+ <% link_to "Edit", edit_task_path(@task) %>
+<% end %>
+<% if @task.user_can_delete?(@user) %>
+ <%= link_to "Delete", task_path(@task), :confirm => "Are you
sure?", :method => :delete %>
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 874f8e9..2f66a65 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -16,10 +16,15 @@
#
ActionController::Routing::Routes.draw do |map|
+ map.resources :tasks
+
# You can have the root of your site routed with map.root -- just remember to
# delete public/index.html.
map.root :controller => 'project'
+ # Login URL
+ map.login "/login", :controller => 'home', :action =>
'login'
+
# Error handling
map.error "/error", :controller => 'home', :action =>
'error'
diff --git a/public/stylesheets/forms.css b/public/stylesheets/forms.css
index 99073f4..1809889 100644
--- a/public/stylesheets/forms.css
+++ b/public/stylesheets/forms.css
@@ -1,3 +1,8 @@
+.formError {
+ color: #ff0000;
+ font-weight: bold;
+}
+
table.edit:before {
color: #ff0000;
content: "<p>* indicates a required field.</p>"
diff --git a/test/functional/task_controller_test.rb
b/test/functional/task_controller_test.rb
deleted file mode 100644
index 92551da..0000000
--- a/test/functional/task_controller_test.rb
+++ /dev/null
@@ -1,233 +0,0 @@
-# task_controller_test.rb
-# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program. If not, see <
http://www.gnu.org/licenses/>.
-#
-
-require File.dirname(__FILE__) + '/../test_helper'
-
-class TaskControllerTest < ActionController::TestCase
- fixtures :backlog_items
- fixtures :users
-
- def setup
- @backlog_item = backlog_items(:owned_backlog_item)
- @task = @backlog_item.tasks.first
- @owner = @backlog_item.owner
- @non_owner = users(:jdonuts)
- @inactive_sprint_item = backlog_items(:owned_inactive_sprint_backlog_item)
-
- flunk "FIXTURES ARE BROKEN" if @owner.id == @non_owner.id
-
- @details = {
- :primary_id => @backlog_item.owner.id,
- :description => 'This is a new task.',
- :hours => '2.5',
- :when => Date.today
- }
- end
-
- # Ensures that an invalid user story causes create to fail.
- #
- def test_create_with_invalid_user_story
- get :create,
- nil,
- {:user_id => users(:mcpierce).id}
-
- assert_redirected_to error_path
-
- get :create,
- {:user_story => 9999},
- {:user_id => users(:mcpierce).id}
-
- assert_redirected_to error_path
- end
-
- # Ensures that an anonymous user cannot create a task.
- #
- def test_create_for_anonymous
- get :create,
- {:backlog_item => @backlog_item.id}
-
- assert_redirected_to :controller => :home, :action => :login
- end
-
- # Ensures that a user who doesn't have authorization aren't able to create
- # tasks.
- #
- def test_create_for_unauthorized_user
- get :create, {:backlog_item => @backlog_item.id}, {:user_id =>
users(:jdonuts).id}
-
- assert_redirected_to :controller => :backlog, :action => :view
- end
-
- # Ensures that tasks cannot be created for a backlog item on an non-active
- # sprint.
- #
- def test_create_for_nonactive_sprint
- count = @inactive_sprint_item.tasks.size
-
- get :create,
- {:backlog_item => @inactive_sprint_item.id},
- {:user_id => @inactive_sprint_item.owner.id}
-
- assert_redirected_to :controller => :backlog, :action => :view, :id =>
@inactive_sprint_item.id
- result = BacklogItem.find_by_id((a)inactive_sprint_item.id)
- assert_equal count, result.tasks.size, 'Tasks should not have been updated'
- end
-
- # Ensures that creating a task works as expected.
- #
- def test_create
- get :create,
- {
- :backlog_item => @backlog_item.id,
- :task => @details
- },
- {:user_id => users(:mcpierce).id}
-
- assert_response :success
- assert assigns['task'], 'Did not create a new task.'
- assert assigns['users'], 'Did not load users.'
- end
-
- # Ensures that tasks are saved corrected.
- #
- def test_create_as_save
- post :create,
- {
- :backlog_item => @backlog_item.id,
- :task => @details
- },
- {:user_id => users(:mcpierce).id}
-
- assert_redirected_to :controller => :backlog, :action => :view
- end
-
- # Ensures that attempting to modify an invalid task fails.
- #
- def test_modify_with_invalid_task
- get :modify, nil, {:user_id => users(:mcpierce).id}
-
- assert_redirected_to error_path
-
- get :modify, {:id => 9999}
-
- assert_redirected_to error_path
- end
-
- # Ensures that anonymous users cannot modify tasks.
- #
- def test_modify_for_anonymous
- get :modify, {:id => tasks(:created_login_view).id}
-
- assert_redirected_to :controller => :home, :action => :login
- end
-
- # Ensures that a user who's not the task owner or backup cannot modify the
- # task.
- #
- def test_modify_for_non_developer
- get :modify,
- {:id => tasks(:created_login_view).id},
- {:user_id => users(:project_admin).id}
-
- assert_redirected_to :controller => :backlog, :action => :view
- end
-
- # Ensures that the owner of the user story is allowed to edit a task against
- # that user story.
- #
- def test_modify_for_user_story_owner
- get :modify,
- {:id => tasks(:created_login_view).id},
- {:user_id => users(:mcpierce).id}
-
- assert_response :success
- end
-
- # Ensures that modifying works as expected.
- #
- def test_modify
- get :modify,
- {:id => tasks(:created_login_view).id},
- {:user_id => tasks(:created_login_view).primary_id}
-
- assert_response :success
- assert assigns['task'], 'Did not load the task.'
- assert_equal tasks(:created_login_view).id, assigns['task'].id, 'Did not
load the right task.'
- assert assigns['users'], 'Did not load the series.'
- end
-
- # Ensures that saving a task after modifying it works as expected.
- #
- def test_modify_as_save
- task = tasks(:created_login_view).attributes
-
- post :modify,
- {:id => tasks(:created_login_view), :task => task},
- {:user_id => tasks(:created_login_view).primary_id}
-
- assert_redirected_to :controller => :backlog, :action => :view
- end
-
- # Ensures that anonymous users can't delete tasks
- #
- def test_delete_as_anonymous
- get :delete
-
- assert_redirected_to :controller => :home, :action => :login
- end
-
- # Ensures that deletion requires a valid task id.
- #
- def test_delete_with_invalid_id
- post :delete,
- {:id => 9999},
- {:user_id => @owner.id}
-
- assert_redirected_to error_path
- end
-
- # Ensures that someone who is not the backlog item owner can't add tasks to
- # it.
- #
- def test_delete_as_non_owner
- post :delete,
- {
- :id => @task.id
- },
- {
- :user_id => @non_owner.id
- }
-
- assert Task.find_by_id((a)task.id), 'Task should not have been deleted.'
- end
-
- # Ensures that the primary can delete a task.
- #
- def test_delete
- post :delete,
- {
- :id => @task.id
- },
- {:user_id => @owner.id}
-
- assert_redirected_to(
- :controller => :backlog,
- :action => :view,
- :id => @task.backlog_item.id)
- assert_equal nil, Task.find_by_id((a)task.id), 'Did not delete the task.'
- end
-end
diff --git a/test/functional/tasks_controller_test.rb
b/test/functional/tasks_controller_test.rb
new file mode 100644
index 0000000..270b308
--- /dev/null
+++ b/test/functional/tasks_controller_test.rb
@@ -0,0 +1,313 @@
+# tasks_controller_test.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TasksControllerTest < ActionController::TestCase
+ fixtures :backlog_items
+ fixtures :users
+
+ def setup
+ @backlog_item = backlog_items(:owned_backlog_item)
+ @task = @backlog_item.tasks.first
+ @owner = @backlog_item.owner
+ @nonowner = users(:jdonuts)
+ raise "Owner and nonowner cannot be the same person!" if @owner.id ==
@nonowner.id
+
+ @inactive_sprint_item = backlog_items(:owned_inactive_sprint_backlog_item)
+
+ @details = {
+ :primary_id => @backlog_item.owner.id,
+ :description => 'This is a new task.',
+ :hours => '2.5',
+ :when => Date.today
+ }
+ end
+
+ # Ensures that viewing tasks when no backlog item is specified fails.
+ #
+ def test_index_without_backlog_item
+ get :index
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that getting the list of tasks for a backlog item works as expected.
+ #
+ def test_index
+ get :index, {:backlog_item => @backlog_item.id}
+
+ assert_response :success
+ assert assigns['backlog_item'], "Failed to load the backlog item."
+ assert_equal @backlog_item.id, assigns['backlog_item'].id,
+ "Did not load the right backlog item."
+ assert assigns['tasks'], "Failed to load the tasks."
+ end
+
+ # Ensures that an error is show if no task id is present.
+ #
+ def test_show_without_task_id
+ get :show
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that creating a task requires the user to be logged in.
+ #
+ def test_new_for_anonymous
+ get :new
+
+ assert_redirected_to login_url
+ end
+
+ # Ensures that creating a task without a backlog item fails.
+ #
+ def test_new_without_backlog_item
+ get :new, nil, {:user_id => @owner.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that a non-owner cannot create tasks for a backlog item.
+ #
+ def test_new_for_nonowner
+ get :new,
+ {:backlog_item => @backlog_item.id},
+ {:user_id => @nonowner.id}
+
+ assert_redirected_to :controller => :backlog, :action => :view, :id =>
@backlog_item.id
+ end
+
+ # Ensures that creating a task works as expected.
+ #
+ def test_new
+ get :new,
+ {:backlog_item => @backlog_item.id},
+ {:user_id => @owner.id}
+
+ assert_response :success
+ assert assigns['task'], "Didn't create a task."
+ assert assigns['users'], "Didn't load the list of users."
+ assert_equal @backlog_item.id, assigns['task'].backlog_item_id,
+ "Didn't set the backlog item correctly."
+ assert_equal @owner.id, assigns['task'].primary_id,
+ "Didn't set the primary developer correctly."
+ end
+
+ # Ensures that attempting to modify an invalid task fails.
+ #
+ def test_edit_with_invalid_task
+ get :edit, nil, {:user_id => users(:mcpierce).id}
+
+ assert_redirected_to error_path
+
+ get :edit, {:id => 9999}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that anonymous users cannot modify tasks.
+ #
+ def test_edit_for_anonymous
+ get :edit, {:id => tasks(:created_login_view).id}
+
+ assert_redirected_to :controller => :home, :action => :login
+ end
+
+ # Ensures that a user who's not the task owner or backup cannot modify the
+ # task.
+ #
+ def test_edit_for_non_developer
+ get :edit,
+ {:id => tasks(:created_login_view).id},
+ {:user_id => users(:project_admin).id}
+
+ assert_redirected_to :controller => :backlog, :action => :view
+ end
+
+ # Ensures that the owner of the user story is allowed to edit a task against
+ # that user story.
+ #
+ def test_edit_for_user_story_owner
+ get :edit,
+ {:id => tasks(:created_login_view).id},
+ {:user_id => users(:mcpierce).id}
+
+ assert_response :success
+ end
+
+ # Ensures that modifying works as expected.
+ #
+ def test_edit
+ get :edit,
+ {:id => tasks(:created_login_view).id},
+ {:user_id => tasks(:created_login_view).primary_id}
+
+ assert_response :success
+ assert assigns['task'], 'Did not load the task.'
+ assert_equal tasks(:created_login_view).id, assigns['task'].id, 'Did not
load the right task.'
+ assert assigns['users'], 'Did not load the series.'
+ end
+
+ # Ensures that an invalid user story causes create to fail.
+ #
+ def test_create_with_invalid_user_story
+ get :create,
+ nil,
+ {:user_id => users(:mcpierce).id}
+
+ assert_redirected_to error_path
+
+ get :create,
+ {:user_story => 9999},
+ {:user_id => users(:mcpierce).id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that an anonymous user cannot create a task.
+ #
+ def test_create_for_anonymous
+ get :create,
+ {:backlog_item => @backlog_item.id}
+
+ assert_redirected_to :controller => :home, :action => :login
+ end
+
+ # Ensures that a user who doesn't have authorization aren't able to create
+ # tasks.
+ #
+ def test_create_for_unauthorized_user
+ get :create, {:backlog_item => @backlog_item.id}, {:user_id =>
users(:jdonuts).id}
+
+ assert_redirected_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+
+ # Ensures that tasks cannot be created for a backlog item on an non-active
+ # sprint.
+ #
+ def test_create_for_nonactive_sprint
+ count = @inactive_sprint_item.tasks.size
+
+ get :create,
+ {:backlog_item => @inactive_sprint_item.id},
+ {:user_id => @inactive_sprint_item.owner.id}
+
+ assert_redirected_to :controller => :backlog, :action => :view, :id =>
@inactive_sprint_item.id
+ result = BacklogItem.find_by_id((a)inactive_sprint_item.id)
+ assert_equal count, result.tasks.size, 'Tasks should not have been updated'
+ end
+
+ # Ensures that tasks are saved corrected.
+ #
+ def test_create
+ post :create,
+ {
+ :backlog_item => @backlog_item.id,
+ :task => @details
+ },
+ {:user_id => users(:mcpierce).id}
+
+ assert_redirected_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ end
+
+ # Ensures that anonymous users can't delete tasks
+ #
+ def test_destroy_as_anonymous
+ get :destroy
+
+ assert_redirected_to :controller => :home, :action => :login
+ end
+
+ # Ensures that deletion requires a valid task id.
+ #
+ def test_destroy_with_invalid_id
+ post :destroy,
+ {:id => 9999},
+ {:user_id => @owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that someone who is not the backlog item owner can't add tasks to
+ # it.
+ #
+ def test_destroy_as_non_owner
+ post :destroy,
+ {
+ :id => @task.id
+ },
+ {
+ :user_id => @nonowner.id
+ }
+
+ assert Task.find_by_id((a)task.id), 'Task should not have been deleted.'
+ end
+
+ # Ensures that the primary can delete a task.
+ #
+ def test_destroy
+ post :destroy,
+ {
+ :id => @task.id
+ },
+ {:user_id => @owner.id}
+
+ assert_redirected_to(
+ :controller => :backlog,
+ :action => :view,
+ :id => @task.backlog_item.id)
+ assert_equal nil, Task.find_by_id((a)task.id), 'Did not delete the task.'
+ end
+
+ # Ensures that an update requires the user be loggeg in.
+ #
+ def test_update_as_anonymous
+ put :update
+
+ assert_redirected_to login_url
+ end
+
+ # Ensures that an update fails without a task id.
+ #
+ def test_update_without_id
+ put :update, {}, {:user_id => @owner.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that an update fails with an invalid id.
+ #
+ def test_update_with_invalid_id
+ put :update,
+ {:id => 9999},
+ {:user_id => @owner.id}
+ end
+
+ # Ensures that a non-owner cannot update a task.
+ #
+ def test_update_as_nonowner
+ put :update,
+ {:id => @task.id, :task => @task.attributes},
+ {:user_id => @nonowner.id}
+
+ assert_redirected_to :controller => :backlog, :action => :view, :id =>
@backlog_item
+ result = Task.find_by_id((a)task.id)
+ assert_equal @task.updated_at, result.updated_at,
+ "Task should not have been updated."
+ end
+end
diff --git a/test/unit/product_test.rb b/test/unit/product_test.rb
index a0df74d..62647db 100644
--- a/test/unit/product_test.rb
+++ b/test/unit/product_test.rb
@@ -25,7 +25,8 @@ class ProductTest < ActiveSupport::TestCase
def setup
@new_product = Product.new(
:project_id => projects(:cairo).id,
- :owner_id => users(:mcpierce).id)
+ :owner_id => users(:mcpierce).id,
+ :name => "Test Product")
@product = products(:cairo_web)
@owner = @product.owner
diff --git a/test/unit/task_test.rb b/test/unit/task_test.rb
index b524127..7cf486d 100644
--- a/test/unit/task_test.rb
+++ b/test/unit/task_test.rb
@@ -1,8 +1,123 @@
+# task_test.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
require File.dirname(__FILE__) + '/../test_helper'
class TaskTest < ActiveSupport::TestCase
- # Replace this with your real tests.
- def test_truth
- assert true
+ fixtures :backlog_items
+ fixtures :sprints
+ fixtures :users
+
+ def setup
+ @backlog_item = backlog_items(:owned_backlog_item)
+ @new_task = Task.new(
+ :primary_id => @backlog_item.owner_id,
+ :description => 'This is a task',
+ :hours => 5,
+ :when => @backlog_item.sprint.start + (@backlog_item.sprint.duration / 2))
+ @new_task.backlog_item = @backlog_item
+ @backup = users(:jdonuts)
+
+ raise "Backup cannot be the owner!" if @backup.id ==
@backlog_item.owner.id
+ end
+
+ # Ensures that at least a primary must be defined.
+ #
+ def test_valid_fails_without_primary
+ @new_task.primary = nil
+
+ flunk "A primary developer must be set." if @new_task.valid?
+ end
+
+ # Ensures that the primary and backup developer cannot be the same person.
+ #
+ def test_valid_fails_if_primary_is_backup
+ @new_task.backup = @new_task.primary
+
+ flunk "The primary developer cannot be the backup." if @new_task.valid?
+ end
+
+ # Ensures that a backlog item is required.
+ #
+ def test_valid_fails_without_backlog_item
+ @new_task.backlog_item = nil
+
+ flunk "A backlog item must be defined." if @new_task.valid?
+ end
+
+ # Ensures that the description must be present.
+ #
+ def test_valid_fails_without_description
+ @new_task.description = ""
+
+ flunk "A description must be provided." if @new_task.valid?
+ end
+
+ # Ensures that hours must be provided.
+ #
+ def test_valid_fails_without_hours
+ @new_task.hours = nil
+
+ flunk "Hours must be specified." if @new_task.valid?
+ end
+
+ # Ensures that hours cannot be negative.
+ #
+ def test_valid_fails_with_negative_hours
+ @new_task.hours = -3
+
+ flunk "Hours must be a positive value." if @new_task.valid?
+ end
+
+ # Ensures that hours cannot be zero.
+ #
+ def test_valid_fails_with_zero_for_hours
+ @new_task.hours = 0
+
+ flunk "Hours cannot be zero." if @new_task.valid?
+ end
+
+ # Ensures that a date must be present.
+ #
+ def test_valid_fails_without_date
+ @new_task.when = nil
+
+ flunk "Date must be set." if @new_task.valid?
+ end
+
+ # Ensures that the date for a task can't be before the sprint starts.
+ #
+ def test_valid_fails_when_date_preceeds_sprint
+ @new_task.when = @backlog_item.sprint.start - 1
+
+ flunk "Date cannot be before the sprint starts." if @new_task.valid?
+ end
+
+ # Ensures that the date for a task can't be after the sprint ends.
+ #
+ def test_valid_fail_when_date_exceeds_sprint
+ @new_task.when = @backlog_item.sprint.start + @backlog_item.sprint.duration
+
+ flunk "Date cannot be after the sprint ends." if @new_task.valid?
+ end
+
+ # Ensures that a well-formed task if valid.
+ #
+ def test_valid
+ flunk "Something is wrong with validation." unless @new_task.valid?
end
end
--
1.6.0.2