NOTE: This patch fixes the bug found when viewing a product.
Added a new resource named :projects.
Renamed ProjectController and ProjectControllerTest to ProjectsController
and ProjectsControllerTest, respectively.
Rewrote the functional tests to make sure they properly vetted the
refactored controller.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/application.rb | 2 +-
app/controllers/product_controller.rb | 2 +-
app/controllers/project_controller.rb | 102 ------------
app/controllers/projects_controller.rb | 142 ++++++++++++++++
app/helpers/application_helper.rb | 57 +++----
app/models/project.rb | 13 ++-
app/views/layouts/default.html.erb | 2 +-
app/views/product/_details.html.erb | 2 +-
app/views/project/_details.html.erb | 25 ---
app/views/project/_edit.html.erb | 59 -------
app/views/project/_product.html.erb | 19 ---
app/views/project/create.html.erb | 1 -
app/views/project/index.html.erb | 48 ------
app/views/project/modify.html.erb | 1 -
app/views/project/view.html.erb | 59 -------
app/views/projects/_details.html.erb | 25 +++
app/views/projects/_edit.html.erb | 58 +++++++
app/views/projects/_product.html.erb | 19 +++
app/views/projects/edit.html.erb | 1 +
app/views/projects/index.html.erb | 56 +++++++
app/views/projects/new.html.erb | 1 +
app/views/projects/show.html.erb | 59 +++++++
app/views/projects/update.html.erb | 1 +
app/views/tasks/new.html.erb | 4 +-
config/routes.rb | 3 +-
test/functional/product_controller_test.rb | 4 +-
test/functional/project_controller_test.rb | 176 --------------------
test/functional/projects_controller_test.rb | 233 +++++++++++++++++++++++++++
28 files changed, 640 insertions(+), 534 deletions(-)
delete mode 100644 app/controllers/project_controller.rb
create mode 100644 app/controllers/projects_controller.rb
delete mode 100644 app/views/project/_details.html.erb
delete mode 100644 app/views/project/_edit.html.erb
delete mode 100644 app/views/project/_product.html.erb
delete mode 100644 app/views/project/create.html.erb
delete mode 100644 app/views/project/index.html.erb
delete mode 100644 app/views/project/modify.html.erb
delete mode 100644 app/views/project/view.html.erb
create mode 100644 app/views/projects/_details.html.erb
create mode 100644 app/views/projects/_edit.html.erb
create mode 100644 app/views/projects/_product.html.erb
create mode 100644 app/views/projects/edit.html.erb
create mode 100644 app/views/projects/index.html.erb
create mode 100644 app/views/projects/new.html.erb
create mode 100644 app/views/projects/show.html.erb
create mode 100644 app/views/projects/update.html.erb
delete mode 100644 test/functional/project_controller_test.rb
create mode 100644 test/functional/projects_controller_test.rb
diff --git a/app/controllers/application.rb b/app/controllers/application.rb
index 3378a90..51261a0 100644
--- a/app/controllers/application.rb
+++ b/app/controllers/application.rb
@@ -34,7 +34,7 @@ class ApplicationController < ActionController::Base
if @user == nil
session[:original_uri] = request.request_uri
flash[:message] = 'Please login first.'
- redirect_to(:controller => :home, :action => :login)
+ redirect_to login_url
end
end
diff --git a/app/controllers/product_controller.rb
b/app/controllers/product_controller.rb
index f5553d3..43c6425 100644
--- a/app/controllers/product_controller.rb
+++ b/app/controllers/product_controller.rb
@@ -51,7 +51,7 @@ class ProductController < ApplicationController
end
else
flash[:message] = 'You are not authorized to create products.'
- redirect_to :controller => :project, :action => :view, :id => @project.id
+ redirect_to project_path(@project)
end
end
diff --git a/app/controllers/project_controller.rb
b/app/controllers/project_controller.rb
deleted file mode 100644
index b122159..0000000
--- a/app/controllers/project_controller.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-# project_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/>.
-#
-
-# +ProjectController+ handles requests relating to the management and viewing of
-# +Product+s.
-#
-class ProjectController < ApplicationController
- before_filter :authenticated, :only => [:create, :modify]
- before_filter :load_project, :except => [:create, :index]
- before_filter :setup_for_edit, :only => [:create, :modify]
-
- # Allows viewing of all defined projects.
- #
- def index
- @projects = Project.paginate :page => params[:page], :per_page => 10
- end
-
- # Shows a single project.
- #
- def view
- @products = Product.paginate_by_project_id @project.id, :page => params[:page],
:per_page => 10
- end
-
- # Begins the process of creating a new project.
- #
- def create
- if @user.create_projects?
- if request.post?
- begin
- Project.transaction do
- @project = Project.new(params[:project])
- @project.save!
-
- redirect_to :action => :index
- end
- rescue ActiveRecord::RecordInvalid => error
- @project.valid?
- render :action => :create
- end
- else
- @project = Project.new
- end
- else
- flash[:message] = 'That action requires administrative rights.'
- redirect_to :controller => :home, :action => :dashboard
- end
- end
-
- # Modifies an existing project.
- #
- def modify
- if @user.can_modify_project?(@project)
- if request.post?
- begin
- Project.transaction do
- @project.update_attributes(params[:project])
- @project.save!
-
- redirect_to :action => :view, :id => @project
- end
- rescue ActiveRecord::RecordInvalid => error
- @project.valid?
- render :action => :modify
- end
- end
- else
- flash[:message] = "You are not authorized to modify that project."
- redirect_to error_path
- end
- end
-
- private
-
- # Loads the requested project.
- #
- def load_project
- @project = Project.find_by_id(params[:id])
-
- unless @project
- flash[:message] = 'Invalid project specified.'
- redirect_to error_path
- end
- end
-
- def setup_for_edit
- @users = User.find(:all, :order => 'display_name ASC')
- end
-end
diff --git a/app/controllers/projects_controller.rb
b/app/controllers/projects_controller.rb
new file mode 100644
index 0000000..6c379b7
--- /dev/null
+++ b/app/controllers/projects_controller.rb
@@ -0,0 +1,142 @@
+# projects_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/>.
+#
+
+# +ProjectsController+ handles requests relating to the management and viewing
+# of +Product+s.
+#
+class ProjectsController < ApplicationController
+ before_filter :authenticated, :only => [:new, :create, :edit, :update]
+ before_filter :load_project, :except => [:index, :new, :create, :destroy]
+ before_filter :setup_for_edit, :only => [:new, :create, :edit]
+
+ # GET /projects
+ def index
+ @projects = Project.paginate :page => params[:page], :per_page => 10
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ # GET /projects/1
+ def show
+ @products = Product.paginate_by_project_id @project.id, :page => params[:page],
:per_page => 10
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ # get /projects/new
+ def new
+ if @user.create_projects?
+ @project = Project.new(:owner_id => @user.id)
+ else
+ flash[:error] = "You are not authorized to create projects."
+
+ redirect_to error_url
+ end
+ end
+
+ # GET /projects/1;edit
+ def edit
+ unless @project.user_can_modify?(@user)
+ flash[:message] = "You are not authorized to modify that project."
+ redirect_to project_path(@project)
+ end
+ end
+
+ # POST /projects
+ def create
+ respond_to do |format|
+ if @user.create_projects?
+ @project = Project.new(params[:project])
+
+ begin
+ Project.transaction do
+ if @project.save
+ flash[:message] = "Project '#{(a)project.name}' created!"
+ format.html { redirect_to project_url(@project) }
+ else
+ @project.valid?
+ format.html { render :action => "new" }
+ end
+ end
+ rescue Exception => error
+ flash[:error] = "ERROR: #{error.message}"
+ format.html { redirect_to error_url }
+ end
+ else
+ flash[:error] = "You are not authorized to create projects."
+ format.html { redirect_to error_url }
+ end
+ end
+ end
+
+ # PUT /projects/1
+ def update
+ respond_to do |format|
+ if @project.user_can_modify?(@user)
+ begin
+ Project.transaction do
+ @project.update_attributes(params[:project])
+ if @project.save!
+ flash[:message] = "Project was successfully updated."
+ format.html { redirect_to project_path(@project) }
+ else
+ @project.valid?
+ format.html { render :action => 'edit'}
+ end
+ end
+ rescue Exception => error
+ flash[:error] = "ERROR: #{error.message}"
+ format.html { redirect_to error_url }
+ end
+ else
+ flash[:error] = "You are not authorized to edit this project."
+ format.html { redirect_to project_path(@project) }
+ end
+ end
+ end
+
+ # DELETE /projects/1
+ def destroy
+ respond_to do |format|
+ flash[:message] = "Project deletion is not current supported."
+ format.html { redirect_to error_url}
+ end
+ end
+
+ private
+
+ # Loads the requested project.
+ #
+ def load_project
+ @project = Project.find_by_id(params[:id])
+
+ unless @project
+ flash[:message] = 'Invalid project specified.'
+ respond_to do |format|
+ format.html { redirect_to error_path }
+ end
+ end
+ end
+
+ def setup_for_edit
+ @users = User.find(:all, :order => 'display_name ASC')
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 96d59a7..88cd918 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,19 +1,19 @@
# application_helper.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/>.
-#
+#
module ApplicationHelper
# Returns the first sentence from the supplied text, up to the first period.
@@ -23,13 +23,13 @@ module ApplicationHelper
if (text == nil) || text.empty?
return " "
end
-
+
return text.slice(/^[^\.]*\.?/)
-
+
end
-
+
# Creates a link to a user.
- #
+ #
def link_to_user(user)
if user
@template.link_to(user.display_name,
@@ -38,9 +38,9 @@ module ApplicationHelper
:id => user)
end
end
-
+
# Creates a link to an owner for a project, product or other item.
- #
+ #
def link_to_owner(owner, args = nil)
if owner
link_to_user(owner)
@@ -49,61 +49,52 @@ module ApplicationHelper
args[:unowned] if args
end
end
-
- # Creates a link to a project.
- #
- def link_to_project(project)
- @template.link_to(project.name,
- :controller => :project,
- :action => :view,
- :id => project)
- end
-
+
# Creates a link to a product.
- #
- def link_to_product(product, options = {})
- @template.link_to(options[:text] ? options[:text] : product.name,
+ #
+ def link_to_product(product)
+ @template.link_to(product.name,
:controller => :product,
- :action => options[:action] ? options[:action] : :view,
+ :action => :view,
:id => product)
end
-
+
# Creates a link to a sprint.
- #
+ #
def link_to_sprint(sprint)
@template.link_to(sprint.title,
:controller => :sprint,
:action => :view,
:id => sprint)
end
-
+
# Displays a link and image reporting the health of the sprint
def show_sprint_health(sprint)
@template.image_tag(
sprint.healthy? ? "/images/icons/healthy.png" :
"/images/icons/unhealthy.png",
:alt => sprint.healthy? ? "Sprint is healthy" : "Sprint is not
healthy")
end
-
+
# Creates a link to a user story.
- #
+ #
def link_to_user_story(user_story, options ={})
@template.link_to((options[:text] ? options[:text] : user_story.title),
:controller => :user_story,
:action => (options[:action] ? options[:action] : :view),
:id => user_story)
end
-
+
# Creates a link to a backlog item.
- #
+ #
def link_to_backlog_item(backlog_item, options = {})
@template.link_to(options[:text] ? options[:text] : backlog_item.title,
:controller => :backlog,
:action => (options[:action] ? options[:action] : :view),
:id => backlog_item.id)
end
-
+
# Shows the hours for a given object as estimated, actual and remaining.
- #
+ #
def show_hours_as_ear(estimated)
format("%0.1f/%0.1f/%0.1f",estimated.estimated_hours,
estimated.actual_hours, estimated.remaining_hours)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 2bd9541..6a7ce5d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -21,14 +21,25 @@
class Project < ActiveRecord::Base
validates_presence_of :name,
:message => 'A project has to have a name.'
-
+ validates_length_of :name,
+ :minimum => 4,
+ :message => 'The project name must be at least four characters long.'
validates_uniqueness_of :name,
:message => 'This name is used by an existing project.'
+ validates_presence_of :owner_id,
+ :message => 'A project must have an owner.'
+
belongs_to :owner, :class_name => 'User', :foreign_key => :owner_id
has_many :products
+ # Returns whether the user can modify this project.
+ #
+ def user_can_modify?(user)
+ user && (user.privileges.admin_projects || (user.id == owner.id))
+ end
+
# Returns whether the user can create products for this project.
#
def can_create_products?(user)
diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb
index ac4dd6f..e1ac558 100644
--- a/app/views/layouts/default.html.erb
+++ b/app/views/layouts/default.html.erb
@@ -24,7 +24,7 @@
<div id="user-box">
<%= link_to "Home", :controller => :home %>
- <%= link_to "Projects", :controller => :project, :action =>
:index %>
+ <%= link_to "Projects", projects_path %>
<%= link_to "Reports", :controller => :report, :action =>
:index %>
<% if @user %>
<%= link_to @user.display_name, dashboard_url %>
diff --git a/app/views/product/_details.html.erb b/app/views/product/_details.html.erb
index d7e1bda..7ea0794 100644
--- a/app/views/product/_details.html.erb
+++ b/app/views/product/_details.html.erb
@@ -14,7 +14,7 @@
<tr>
<td class="label">For Project:</td>
- <td class="value"><%= link_to_project @product.project
%></td>
+ <td class="value"><%= link_to @product.project.name,
project_path((a)product.project) %></td>
</tr>
<tr>
diff --git a/app/views/project/_details.html.erb b/app/views/project/_details.html.erb
deleted file mode 100644
index 7dfc64c..0000000
--- a/app/views/project/_details.html.erb
+++ /dev/null
@@ -1,25 +0,0 @@
-<table class="details">
- <thead>
- <tr>
- <th class="title" colspan="2"><%= "Details For
#{(a)project.name}" %></th>
- </tr>
- </thead>
-
- <tbody>
- <tr>
- <td class="label">Owner:</td>
- <td class="value"><%= link_to_user @project.owner
%></td>
- </tr>
-
- <tr>
- <td class="label">Homepage:</td>
- <td class="value"><%= link_to_homepage @project %>
- </tr>
-
- <tr>
- <td class="text" colspan="2">
- <%= simple_format @project.description %>
- </td>
- </tr>
- </tbody>
-</table>
diff --git a/app/views/project/_edit.html.erb b/app/views/project/_edit.html.erb
deleted file mode 100644
index d1b02e9..0000000
--- a/app/views/project/_edit.html.erb
+++ /dev/null
@@ -1,59 +0,0 @@
-<% form_for :project do |form| %>
-
- <% unless @project.new_record? %>
- <%= form.hidden_field :id %>
- <% end %>
-
- <table class="edit">
- <tbody>
- <tr>
- <td class="label">Project name</td>
- <td class="value"><%= form.text_field :name %></td>
- </tr>
- <tr>
- <td /><td class="errors"><%= error_message_on(:project,
:name) %>
- </tr>
-
- <tr>
- <td class="label">Website</td>
- <td class="value"><%= form.text_field :url %></td>
- </tr>
- <tr>
- <td /><td class="errors"><%= error_message_on(:project,
:url) %>
- </tr>
-
- <tr>
- <td class="label">Owner</td>
- <td class="value">
- <% if @user.privileges.admin_projects %>
- <%= collection_select :project, :owner_id, @users, :id,
- :display_name, {:include_blank => true} %>
- <% else %>
- <% if @project.owner %>
- <%= h @project.owner.display_name %>
- <% else %>
- UNOWNED
- <% end %>
- <% end %>
- </td>
- <td /><td class="errors"><%= error_message_on(:project,
:owner) %>
- </tr>
-
- <tr>
- <td class="label">Description</td>
- <td class="value"><%= form.text_area :description
%></td>
- </tr>
-
- <tr>
- <td class="buttons" colspan="2">
- <% if @project.new_record? %>
- <%= submit_tag "Create", :class => 'button' %>
- <% else %>
- <%= submit_tag "Update", :class => 'button' %>
- <% end %>
- </td>
- </tr>
- </tbody>
- </table>
-
-<% end %>
\ No newline at end of file
diff --git a/app/views/project/_product.html.erb b/app/views/project/_product.html.erb
deleted file mode 100644
index 51766eb..0000000
--- a/app/views/project/_product.html.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-<tr>
- <td><%= link_to "#{product.name}",
- {:controller => :product, :action => :view, :id => product} %>
- </td>
- <td>
- <% if product.owner != nil %>
- <%= link_to(product.owner.display_name,
- {:controller => :user,
- :action => :view,
- :id => product.owner})%>
-
- (<%= mail_to(product.owner.email, "email") %>)
- <% else %>
- None (want to become the owner?)
- <% end %>
- </td>
- <td class="description"><%= h product.description %></td>
- <td class="number"><%= product.backlog.size %></td>
-</tr>
diff --git a/app/views/project/create.html.erb b/app/views/project/create.html.erb
deleted file mode 100644
index 0ae005d..0000000
--- a/app/views/project/create.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= render(:partial => 'edit', :object => @project) %>
diff --git a/app/views/project/index.html.erb b/app/views/project/index.html.erb
deleted file mode 100644
index d472b78..0000000
--- a/app/views/project/index.html.erb
+++ /dev/null
@@ -1,48 +0,0 @@
-<div class="toolbar">
- <% if @user && @user.create_projects? %>
- <%= link_to "New Project", :action => :create %>
- <% end %>
-</div>
-
-<%= will_paginate @projects %>
-
-<table class="list">
- <colgroup>
- <col class="row_id" />
- <col class="name" />
- <col class="description" />
- <col class="user" />
- <col class="date" />
- </colgroup>
- <thead>
- <tr>
- <th class="title" colspan="5">Projects</th>
- </tr>
- <tr>
- <th>##</th>
- <th>Name</th>
- <th>Description</th>
- <th>Owner</th>
- <th>Created</th>
- </tr>
- </thead>
-
- <tbody>
- <% if @projects.empty? %>
- <tr><td colspan="5">There are no projects
defined.</td></tr>
- <% else %>
- <% @projects.each_with_index do |project, i| %>
- <% row_class = i%2 == 0 ? 'even' : 'odd' %>
-
- <tr class="<%= row_class %>">
- <td class="row_id"><%= i + 1 %></td>
- <td><%= link_to_project project %></td>
- <td><%= get_first_sentence project.description %></td>
- <td><%= link_to_owner project.owner, 'Current Unowned'
%></td>
- <td><%= project.created_at.to_s(:date) %></td>
- </tr>
-
- <% end %>
- <% end %>
- </tbody>
-</table>
diff --git a/app/views/project/modify.html.erb b/app/views/project/modify.html.erb
deleted file mode 100644
index 0ae005d..0000000
--- a/app/views/project/modify.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= render(:partial => 'edit', :object => @project) %>
diff --git a/app/views/project/view.html.erb b/app/views/project/view.html.erb
deleted file mode 100644
index c501d67..0000000
--- a/app/views/project/view.html.erb
+++ /dev/null
@@ -1,59 +0,0 @@
-<div style="width: 100%; overflow: auto;">
- <div id="details" style="float: left; width: 33%;">
- <div class="toolbar">
- <% if @user && @user.can_modify_project?(@project) %>
- <%= link_to "Edit project...", :action => :modify, :id =>
@project %>
- <% end %>
- </div>
- <%= render :partial => 'details', :object => @project %>
- </div>
-
- <div id="products" style="float: right; width: 66%;">
- <div class="toolbar">
- <%= will_paginate @products %>
- <% if @project.can_create_products?(@user) %>
- <%= link_to "Create Product...",
- {
- :controller => :product,
- :action => :create,
- :project => @project
- } %>
- <% end %>
- </div>
- <table class="list">
- <colgroup>
- <col class="row_id" />
- <col class="name" />
- <col class="description" />
- <col class="user" />
- <col class="date" />
- </colgroup>
-
- <thead>
- <tr>
- <th>##</th>
- <th>Product</th>
- <th>Description</th>
- <th>Owner</th>
- <th>Created</th>
- </tr>
- </thead>
-
- <tbody>
- <% @products.each_with_index do |product, i| %>
- <% row_class = i%2 == 0 ? 'even' : 'odd' %>
-
- <tr class="<%= row_class %>"
- onmouseover="this.className='hot'"
- onmouseout="this.className='<%= row_class %>'
">
- <td class="row_id"><%= i + 1 %></td>
- <td><%= link_to_product product %></td>
- <td><%= get_first_sentence product.description %></td>
- <td><%= link_to_owner product.owner %></td>
- <td><%= product.created_at.to_s(:date) %></td>
- </tr>
- <% end %>
- </tbody>
- </table>
- </div>
-</div>
diff --git a/app/views/projects/_details.html.erb b/app/views/projects/_details.html.erb
new file mode 100644
index 0000000..9fe7b4b
--- /dev/null
+++ b/app/views/projects/_details.html.erb
@@ -0,0 +1,25 @@
+<table class="details">
+ <thead>
+ <tr>
+ <th class="title" colspan="2"><%= "Details For
#{(a)project.name}" %></th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr>
+ <td class="label">Owner:</td>
+ <td class="value"><%= link_to_user @project.owner
%></td>
+ </tr>
+
+ <tr>
+ <td class="label">Homepage:</td>
+ <td class="value"><%= link_to_homepage @project %>
+ </tr>
+
+ <tr>
+ <td class="text" colspan="2">
+ <%= simple_format @project.description %>
+ </td>
+ </tr>
+ </tbody>
+</table>
diff --git a/app/views/projects/_edit.html.erb b/app/views/projects/_edit.html.erb
new file mode 100644
index 0000000..0b5d409
--- /dev/null
+++ b/app/views/projects/_edit.html.erb
@@ -0,0 +1,58 @@
+<% html = @project.new_record? ? {:method => :post} : {:method => :put} %>
+
+<% form_for :project, @project, :url => project_path(@project), :html => html do
|form| %>
+ <table class="edit">
+ <tbody>
+ <tr>
+ <td class="label">Project name</td>
+ <td class="value">
+ <%= form.text_field :name %>
+ <%= error_message_on(:project, :name) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Website</td>
+ <td class="value">
+ <%= form.text_field :url %>
+ <%= error_message_on(:project, :url) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Owner</td>
+ <td class="value">
+ <% if @user.privileges.admin_projects %>
+ <%= collection_select :project, :owner_id, @users, :id,
+ :display_name, {:include_blank => true} %>
+ <% else %>
+ <% if @project.owner %>
+ <%= h @project.owner.display_name %>
+ <% else %>
+ UNOWNED
+ <% end %>
+ <% end %>
+ <%= error_message_on(:project, :owner) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label">Description</td>
+ <td class="value">
+ <%= form.text_area :description %>
+ <%= error_message_on(:project, :description) %>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="buttons" colspan="2">
+ <% if @project.new_record? %>
+ <%= submit_tag "Create", :class => 'button' %>
+ <% else %>
+ <%= submit_tag "Update", :class => 'button' %>
+ <% end %>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+<% end %>
\ No newline at end of file
diff --git a/app/views/projects/_product.html.erb b/app/views/projects/_product.html.erb
new file mode 100644
index 0000000..51766eb
--- /dev/null
+++ b/app/views/projects/_product.html.erb
@@ -0,0 +1,19 @@
+<tr>
+ <td><%= link_to "#{product.name}",
+ {:controller => :product, :action => :view, :id => product} %>
+ </td>
+ <td>
+ <% if product.owner != nil %>
+ <%= link_to(product.owner.display_name,
+ {:controller => :user,
+ :action => :view,
+ :id => product.owner})%>
+
+ (<%= mail_to(product.owner.email, "email") %>)
+ <% else %>
+ None (want to become the owner?)
+ <% end %>
+ </td>
+ <td class="description"><%= h product.description %></td>
+ <td class="number"><%= product.backlog.size %></td>
+</tr>
diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb
new file mode 100644
index 0000000..0ae005d
--- /dev/null
+++ b/app/views/projects/edit.html.erb
@@ -0,0 +1 @@
+<%= render(:partial => 'edit', :object => @project) %>
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
new file mode 100644
index 0000000..d4008f5
--- /dev/null
+++ b/app/views/projects/index.html.erb
@@ -0,0 +1,56 @@
+<div class="toolbar">
+ <% if @user && @user.create_projects? %>
+ <%= link_to "New Project", new_project_path %>
+ <% end %>
+</div>
+
+<%= will_paginate @projects %>
+
+<table class="list">
+ <colgroup>
+ <col class="row_id" />
+ <col class="name" />
+ <col class="description" />
+ <col class="user" />
+ <col class="date" />
+ <col class="actions" />
+ </colgroup>
+ <thead>
+ <tr>
+ <th class="title" colspan="6">Projects</th>
+ </tr>
+ <tr>
+ <th>##</th>
+ <th>Name</th>
+ <th>Description</th>
+ <th>Owner</th>
+ <th>Created</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <% if @projects.empty? %>
+ <tr><td colspan="6">There are no projects
defined.</td></tr>
+ <% else %>
+ <% @projects.each_with_index do |project, i| %>
+ <% row_class = i%2 == 0 ? 'even' : 'odd' %>
+
+ <tr class="<%= row_class %>">
+ <td class="row_id"><%= i + 1 %></td>
+ <td><%= link_to project.name, project_path(project) %></td>
+ <td><%= get_first_sentence project.description %></td>
+ <td><%= link_to_owner project.owner, 'Current Unowned'
%></td>
+ <td><%= project.created_at.to_s(:date) %></td>
+ <td>
+ <%= link_to "View", project_path(project) %>
+ <% if project.user_can_modify?(@user) %>
+ <%= link_to "Edit", edit_project_path(project) %>
+ <% end %>
+ </td>
+ </tr>
+
+ <% end %>
+ <% end %>
+ </tbody>
+</table>
diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb
new file mode 100644
index 0000000..0ae005d
--- /dev/null
+++ b/app/views/projects/new.html.erb
@@ -0,0 +1 @@
+<%= render(:partial => 'edit', :object => @project) %>
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
new file mode 100644
index 0000000..f8fe791
--- /dev/null
+++ b/app/views/projects/show.html.erb
@@ -0,0 +1,59 @@
+<div style="width: 100%; overflow: auto;">
+ <div id="details" style="float: left; width: 33%;">
+ <div class="toolbar">
+ <% if @project.user_can_modify?(@user) %>
+ <%= link_to "Edit", edit_project_path(@project)%>
+ <% end %>
+ </div>
+ <%= render :partial => 'details', :object => @project %>
+ </div>
+
+ <div id="products" style="float: right; width: 66%;">
+ <div class="toolbar">
+ <%= will_paginate @products %>
+ <% if @project.can_create_products?(@user) %>
+ <%= link_to "Create Product...",
+ {
+ :controller => :product,
+ :action => :create,
+ :project => @project
+ } %>
+ <% end %>
+ </div>
+ <table class="list">
+ <colgroup>
+ <col class="row_id" />
+ <col class="name" />
+ <col class="description" />
+ <col class="user" />
+ <col class="date" />
+ </colgroup>
+
+ <thead>
+ <tr>
+ <th>##</th>
+ <th>Product</th>
+ <th>Description</th>
+ <th>Owner</th>
+ <th>Created</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <% @products.each_with_index do |product, i| %>
+ <% row_class = i%2 == 0 ? 'even' : 'odd' %>
+
+ <tr class="<%= row_class %>"
+ onmouseover="this.className='hot'"
+ onmouseout="this.className='<%= row_class %>'
">
+ <td class="row_id"><%= i + 1 %></td>
+ <td><%= link_to_product product %></td>
+ <td><%= get_first_sentence product.description %></td>
+ <td><%= link_to_owner product.owner %></td>
+ <td><%= product.created_at.to_s(:date) %></td>
+ </tr>
+ <% end %>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/app/views/projects/update.html.erb b/app/views/projects/update.html.erb
new file mode 100644
index 0000000..0ae005d
--- /dev/null
+++ b/app/views/projects/update.html.erb
@@ -0,0 +1 @@
+<%= render(:partial => 'edit', :object => @project) %>
diff --git a/app/views/tasks/new.html.erb b/app/views/tasks/new.html.erb
index ae21641..ee1119f 100644
--- a/app/views/tasks/new.html.erb
+++ b/app/views/tasks/new.html.erb
@@ -1,5 +1,3 @@
<div>
- <% form_for(:task, @task, :url => task_path(@task)) do |form| %>
- <%= render :partial => 'edit', :object => @task %>
- <% end %>
+ <%= render(:partial => 'edit', :object => @project) %>
</div>
diff --git a/config/routes.rb b/config/routes.rb
index 2f66a65..c1f20fc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -16,11 +16,12 @@
#
ActionController::Routing::Routes.draw do |map|
+ map.resources :projects
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'
+ map.root :controller => 'home'
# Login URL
map.login "/login", :controller => 'home', :action =>
'login'
diff --git a/test/functional/product_controller_test.rb
b/test/functional/product_controller_test.rb
index acca9e7..f008cbb 100644
--- a/test/functional/product_controller_test.rb
+++ b/test/functional/product_controller_test.rb
@@ -84,7 +84,7 @@ class ProductControllerTest < ActionController::TestCase
{:project => @project.id},
{:user_id => users(:jdonuts).id}
- assert_redirected_to :controller => :project, :action => :view, :id =>
@project.id
+ assert_redirected_to project_url(@project)
end
# Ensures that someone with project admin rights but who isn't the project
@@ -95,7 +95,7 @@ class ProductControllerTest < ActionController::TestCase
{:project => @project.id},
{:user_id => @non_project_owner.id}
- assert_redirected_to :controller => :project, :action => :view, :id =>
@project.id
+ assert_redirected_to project_url(@project)
end
# Ensures that calling create initially works as expected.
diff --git a/test/functional/project_controller_test.rb
b/test/functional/project_controller_test.rb
deleted file mode 100644
index 347e795..0000000
--- a/test/functional/project_controller_test.rb
+++ /dev/null
@@ -1,176 +0,0 @@
-# project_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'
-
-# +ProjectControllerTest+ performs tests on +ProjectController+ to ensure that
-# it works as expected.
-#
-class ProjectControllerTest < ActionController::TestCase
- fixtures :projects
- fixtures :users
-
- def setup
- @project = projects(:cairo)
- @admin = @project.owner
- @nonadmin = users(:jdonuts)
-
- raise "Admin and non-admin cannot be the same person." if @admin.id ==
@nonadmin.id
- end
-
- # Ensures that viewing the list of projects works as expected.
- #
- def test_index
- get :index
-
- assert_response :success
- assert assigns['projects']
- end
-
- # Ensures that viewing without a project id shows an error.
- #
- def test_view_without_id
- get :view
-
- assert_redirected_to error_path
- end
-
- # Ensures that viewing a project works as expected.
- #
- def test_view
- get :view, {:id => @project.id}
-
- assert_response :success
- assert_equal 2, assigns['products'].size,
- 'Did not load the set of products.'
- end
-
- # Ensures that an anonymous user cannot create a new project.
- #
- def test_create_for_anonymous_user
- get :create
-
- assert_redirected_to :controller => :home, :action => :login
- end
-
- # Ensures that a user who does not have project admin rights cannot create a
- # new project.
- #
- def test_create_for_non_admin
- get :create,
- nil,
- {:user_id => @nonadmin.id}
-
- assert_redirected_to :controller => :home, :action => :dashboard
- end
-
- # Ensures that create works as expected.
- #
- def test_create
- get :create,
- {},
- {:user_id => @admin.id}
-
- assert_response :success
- assert assigns['project']
- assert assigns['users']
- end
-
- # Ensures the controller redisplays the edit form when the project is invalid.
- #
- def test_create_as_save_with_invalid_project
- post :create, {:project => {:owner_id => @admin.id}}, {:user_id =>
@admin.id}
-
- assert_response :success
- assert_template 'project/create'
- end
-
- # Ensures that saving a project works as expected.
- #
- def test_create_as_save
- project_details = {
- :name => 'project1',
- :owner_id => @admin.id,
- :url => 'http://www.project.org/',
- :description => 'This is a new project'
- }
-
- post :create,
- {:project => project_details},
- {:user_id => @admin.id}
-
- assert_redirected_to :action => :index
-
- result = Project.find_by_name(project_details[:name])
-
- assert result, 'Project was not saved.'
- end
-
- # Ensures that anonymous users cannot modify a project.
- #
- def test_modify_for_anonymous_user
- get :modify
-
- assert_redirected_to :controller => :home, :action => :login
- end
-
- # Ensures that modifying an invalid project results in an error.
- #
- def test_modify_for_invalid_project
- post :modify,
- {:id => 9999},
- {:user_id => @admin.id}
-
- assert_redirected_to error_path
- end
-
- # Ensures that unauthorized users cannot modify a project.
- #
- def test_modify_for_unauthorized_user
- get :modify,
- {:id => @project.id},
- {:user_id => @nonadmin.id}
-
- assert_redirected_to error_path
- end
-
- # Ensures that starting to modify a project works as expected.
- #
- def test_modify
- get :modify,
- {:id => @project.id},
- {:user_id => @project.owner_id}
-
- assert_response :success
- end
-
- # Ensures that modifying an existing project works as expected.
- #
- def test_modify_as_save
- project_details = {:name => 'newname1'}
-
- post :modify,
- {:id => @project.id, :project => project_details},
- {:user_id => @project.owner_id}
-
- assert_redirected_to :action => :view
-
- result = Project.find_by_id((a)project.id)
-
- assert_equal project_details[:name], result.name, 'Project was not
modified.'
- end
-end
diff --git a/test/functional/projects_controller_test.rb
b/test/functional/projects_controller_test.rb
new file mode 100644
index 0000000..2e3e303
--- /dev/null
+++ b/test/functional/projects_controller_test.rb
@@ -0,0 +1,233 @@
+# projects_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'
+
+# +ProjectsControllerTest+ performs tests on +ProjectController+ to ensure that
+# it works as expected.
+#
+class ProjectsControllerTest < ActionController::TestCase
+ fixtures :projects
+ fixtures :users
+
+ def setup
+ @project = projects(:cairo)
+ @owner = @project.owner
+ @nonowner = users(:jdonuts)
+ raise "Owner and nonowner cannot be the same user!" if @owner.id ==
@nonowner.id
+
+ @nonadmin = @nonowner
+ @admin = users(:project_admin)
+ raise "Nonadmin and admin cannot be the same user!" if @nonadmin.id ==
@admin.id
+ end
+
+ # Ensures that showing the project index works as expected.
+ #
+ def test_index
+ get :index
+
+ assert_response :success
+ assert assigns['projects'], "Didn't load any projects."
+ end
+
+ # Ensures that showing a single project requires an id.
+ #
+ def test_show_without_id
+ get :show
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that showing a project fails on invalid ids.
+ #
+ def test_show_with_invalid_id
+ get :show, {:id => 9999}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures tha show a project works as expected.
+ #
+ def test_show
+ get :show, {:id => @project.id}
+
+ assert_response :success
+ assert assigns['project'], "Failed to load the project."
+ assert_equal @project.id, assigns['project'].id,
+ "Failed to load the correct project."
+ end
+
+ # Ensures that an anonymous user cannot create a new project.
+ #
+ def test_new_as_anonymous
+ get :new
+
+ assert_redirected_to :controller => :home, :action => :login
+ end
+
+ # Ensures that a user without project admin privileges cannot create a
+ # project.
+ #
+ def test_new_as_unauthorized_user
+ get :new, {}, {:user_id => @nonadmin.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that starting off a new project works as expected.
+ #
+ def test_new
+ get :new, {}, {:user_id => @admin.id}
+
+ assert_response :success
+ assert assigns['project'], "Did not create a new project."
+ assert_equal @admin.id, assigns['project'].owner_id,
+ "Did not assign the default owner."
+ assert assigns['users'], "Did not load the list of users."
+ end
+
+ # Ensures an anonymous user cannot start editing a project.
+ #
+ def test_edit_as_anonymous
+ get :edit
+
+ assert_redirected_to :controller => :home, :action => :login
+ end
+
+ # Ensures that edit fails on an invalid id.
+ #
+ def test_edit_with_invalid_id
+ get :edit,
+ {:id => 9999},
+ {:user_id => @owner.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that a nonowner cannot edit a project.
+ #
+ def test_edit_as_nonowner
+ get :edit,
+ {:id => @project.id},
+ {:user_id => @nonowner.id}
+
+ assert_redirected_to project_path(@project)
+ end
+
+ # Ensures that the owner can edit a project.
+ #
+ def test_edit
+ get :edit,
+ {:id => @project.id},
+ {:user_id => @owner.id}
+
+ assert_response :success
+ assert assigns['project'], "Failed to load a project."
+ assert_equal @project.id,assigns['project'].id,
+ "Failed to load the correct project."
+ end
+
+ # Ensures that an anonymous user cannot create a project.
+ #
+ def test_create_as_anonymous
+ post :create
+
+ assert_redirected_to login_url
+ end
+
+ # Ensures that a non-admin cannot create a project.
+ #
+ def test_create_as_nonadmin
+ post :create, {}, {:user_id => @nonadmin.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that an invalid project returns the user to the edit page.
+ #
+ def test_create_with_invalid_project
+ post :create,
+ {:project => {}},
+ {:user_id => @admin.id}
+
+ assert_response :success
+ end
+
+ # Ensures that a project is created when one is submitted.
+ #
+ def test_create
+ post :create,
+ {:project => {:name => 'test', :owner_id => @admin.id}},
+ {:user_id => @admin.id}
+
+ assert_redirected_to project_path(assigns['project'])
+ end
+
+ # Ensures that an anonymous user cannot update a project.
+ #
+ def test_update_as_anonymous
+ put :update
+
+ assert_redirected_to login_url
+ end
+
+ # Ensures that a valid project id must be provided.
+ #
+ def test_update_with_invalid_id
+ put :update,
+ {:id => 9999},
+ {:user_id => @admin.id}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that a nonadmin cannot update a project.
+ #
+ def test_update_by_nonowner
+ put :update,
+ {:id => @project.id,
+ :project => {:name => 'newname'}},
+ {:user_id => @nonowner.id}
+
+ assert_redirected_to project_path(@project)
+ result = Project.find_by_id((a)project.id)
+ assert_equal @project.name, result.name,
+ "Project should not have been updated!"
+ end
+
+ # Ensures that the owner can edit a project.
+ #
+ def test_update
+ put :update,
+ {:id => @project.id,
+ :project => {:name => 'newname'}},
+ {:user_id => @owner.id}
+
+ assert_redirected_to project_path(@project)
+ result = Project.find_by_id((a)project.id)
+ assert_equal 'newname', result.name,
+ "Project should have been updated!"
+ end
+
+ # Ensures that the destroy method just exists.
+ #
+ def test_destroy
+ delete :destroy
+
+ assert_redirected_to error_url
+ end
+end
--
1.6.0.2