Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed a couple of story issues

I have fixed a couple of story issues.  1) Title field in the db was set
to 40 characters but the slug was 255. This meant that the
Story.to_param value wasn't the same as the slug which caused problems.
2) The aasm_event :back_to_new event needed from :new added to it.  Also
some minor formatting stuff.
  • Loading branch information...
commit f7a2accab82942d1c5997b599e596014c1152a37 1 parent df004ab
@sleepingstu sleepingstu authored
View
5 app/controllers/stories_controller.rb
@@ -15,10 +15,12 @@
class StoriesController < ApplicationController
layout 'main'
+
before_filter :login_or_password_required, :only => [:show, :generate_feature]
before_filter :login_required, :except => [:show, :generate_feature]
before_filter :retrieve_iterations, :except => [:generate_feature]
before_filter :load_iteration, :except => [:generate_feature, :show]
+
in_place_edit_for :story, :title
in_place_edit_for :story, :description
in_place_edit_for :story, :points
@@ -40,6 +42,7 @@ def index
@quality_assurance_stories = Story.for_iteration(@iteration.id).in_quality_assurance
@completed_stories = Story.for_iteration(@iteration.id).completed
@total_assigned_points = 0
+
Story.for_iteration(@iteration.id).map { |s|
@total_assigned_points += s.points
}
@@ -141,12 +144,12 @@ def destroy
@story.destroy
respond_to do |format|
+ flash[:notice] = 'Story was successfully deleted.'
format.html { redirect_to iteration_stories_path(@iteration) }
format.xml { head :ok }
end
end
-
private
def retrieve_iterations
View
5 app/models/story.rb
@@ -18,7 +18,7 @@ class Story < ActiveRecord::Base
# Validations
#
- validates_presence_of :description, :points, :title
+ validates_presence_of :title, :description, :points
validates_uniqueness_of :title
# Associations
@@ -74,7 +74,7 @@ class Story < ActiveRecord::Base
end
aasm_event :back_to_new do
- transitions :from => :in_progress, :to => :new
+ transitions :from => [:new, :in_progress], :to => :new
end
attr_protected :status
@@ -84,6 +84,7 @@ def to_param
end
private
+
def set_slug
self.slug = self.to_param
end
View
2  app/views/iterations/_burndown_chart.html.erb
@@ -18,4 +18,4 @@
<h2>Burndown chart for <%= @iteration.name %></h2>
-<div id="burndown-chart"></div>
+<div id="burndown-chart"></div>
View
2  app/views/layouts/_header.html.erb
@@ -21,12 +21,14 @@
<li <%= tab_on("login") %>><%= link_to 'Log In', login_path %></li>
<% end -%>
</ul>
+ <% if logged_in? -%>
<form method="get" action="">
<fieldset>
<input type="text" name="s" id="search-text" size="15" />
<input type="submit" id="search-submit" value="GO" />
</fieldset>
</form>
+ <% end -%>
</div>
<!-- end #menu -->
</div>
View
10 app/views/layouts/main.html.erb
@@ -15,7 +15,15 @@
<%= render :partial => "layouts/header" %>
<% if flash[:notice] %>
- <div id="flash-notice"><%= flash[:notice] %></div>
+ <div id="flash-notice">
+ <!-- Extra div needed for SlideUp effect. -->
+ <div>
+ <%= flash[:notice] %>
+ </div>
+ </div>
+ <script type="text/javascript">
+ Util.FadeFlash();
+ </script>
<% end %>
<div id="content">
View
53 app/views/stories/_form.html.erb
@@ -1,27 +1,32 @@
+<%= error_messages_for :object => @story,
+ :header_tag => 'h3',
+ :id => "errors",
+ :class => "errors",
+ :header_message => "Whoops!",
+ :message => "We had some problems saving this story."
+-%>
+<p class="form-fields">
+ <%= f.label :title %>
+ <%= f.text_field :title, :size => 52 %>
+</p>
+<p class="form-fields">
+ <%= f.label :description %>
+ <%= f.text_area :description, :size => "60x8" %>
+</p>
+<% unless params[:action] = "new" %>
+<p class="form-fields">
+ <%= f.label :status %>
+ <%= f.select :status, ['new', 'in_progress', 'quality_assurance', 'completed'] %>
+</p>
+<% end %>
+<p class="form-fields">
+ <%= f.label :points %>
+ <%= f.select :points, (1..10).to_a %>
+</p>
+<% unless params[:action] = "new" %>
<p class="form-fields">
- <%= f.label :title %>
- <%= f.text_field :title, :size => 52 %>
+ <%= f.label :iteration %>
+ <%= f.select :iteration_id, @iterations.map {|iteration| [iteration.name, iteration.id]}.insert(0,["Not Assigned",'']) %>
</p>
- <p class="form-fields">
- <%= f.label :description %>
- <%= f.text_area :description, :size => "60x8" %>
- </p>
- <% unless params[:action] = "new" %>
- <p class="form-fields">
- <%= f.label :status %>
- <%= f.select :status, ['new', 'in_progress', 'quality_assurance', 'completed'] %>
- </p>
- <% end %>
- <p class="form-fields">
- <%= f.label :points %>
- <%= f.select :points, (1..10).to_a %>
- </p>
- <% unless params[:action] = "new" %>
- <p class="form-fields">
- <%= f.label :iteration %>
- <%= f.select :iteration_id, @iterations.map {|iteration| [iteration.name, iteration.id]}.insert(0,["Not Assigned",'']) %>
- </p>
- <% else %>
- <%=f.hidden_field :iteration_id %>
- <% end %>
+<% end %>
View
95 app/views/stories/_story.html.erb
@@ -1,60 +1,55 @@
-<!-- stories/_story.html.erb -->
- <% @story = story -%>
- <div id="<%= element_id(story) %>" class="story">
- <div class="accordion-toggle<%= ' accordion-toggle-active' if @active %>">
- <%= in_place_editor_field 'story', 'title', {}, :url => {
- :action => "set_story_title", :id => @story.id,
- :iteration_id => @iteration.to_param } %> &nbsp;
- <%= link_to image_tag("/images/icons/delete_icon_20x19.png", :alt => "Delete"),
- iteration_story_path(@iteration, @story), :confirm => 'Delete this story?',
- :method => :delete, :class => "delete-small" %>
- <div style="clear:left">By <%=@story.author%>
- <%= clippy("branston -g -f #{@story.to_param} -b #{request.host} -p #{request.port} ", "#FFFF99") %></div>
- </div>
+<% @story = story -%>
+<div id="<%= element_id(story) %>" class="story">
+ <div class="accordion-toggle<%= ' accordion-toggle-active' if @active %>">
+ <h3><%= in_place_editor_field 'story', 'title' %> <%= clippy("branston -g -f #{@story.to_param} -b #{request.host} -p #{request.port} ", "#FFFF99") %></h3>
+ <%= link_to "Delete", iteration_story_path(@iteration, @story),
+ :confirm => 'Delete this story?', :method => :delete, :class => "delete" %>
+ </div>
- <div id="<%= element_id(story,'scenarios') %>" class="accordion-content">
- <div class="story-props">
- <% remote_form_for :story, @story, :url => "#{iteration_story_path(@iteration, @story)}.js" do |f| -%>
- <%= hidden_field_tag '_method', 'PUT' %>
- <%= hidden_field_tag 'id', @story.id %>
- <div>
- <%= f.select :points, (1..10).to_a, {}, :id => element_id(@story,'points') %> Points
- </div>
- <div>
- <%= f.label :status %>
- <%= f.select :status, ['new', 'in_progress', 'quality_assurance', 'completed'].map {
- |s| [s.capitalize.gsub('_', ' '), s]}, {},
- :id => element_id(@story, 'status') %>
- </div>
- <% unless @iterations.empty? -%>
- <div>
- <%= f.label :iteration_id, "Iteration" %>
- <%= f.select :iteration_id, @iterations.map{|i|[i.name,i.id]},
- {:prompt => "Assign Iteration"}, :id => element_id(@story,'iterations') %>
- </div>
- <% end -%>
- <% end -%>
- </div>
+ <div id="<%= element_id(story,'scenarios') %>" class="accordion-content">
+ <div class="story-props">
+ <% remote_form_for :story, @story, :url => "#{iteration_story_path(@iteration, @story)}.js" do |f| -%>
+ <%= hidden_field_tag '_method', 'PUT' %>
+ <%= hidden_field_tag 'id', @story.id %>
<p>
- <%= in_place_editor_field 'story', 'description' %>
+ <%= f.label :points %>
+ <%= f.select :points, (1..10).to_a, {}, :id => element_id(@story,'points') %>
</p>
-
- <div>
- <%= link_to_remote "Scenarios &raquo;", :method => 'GET',
- :url => iteration_story_scenarios_path(@iteration, @story) %>
- </div>
- </div>
+ <p>
+ <%= f.label :status %>
+ <%= f.select :status, ['new', 'in_progress', 'quality_assurance', 'completed'].map {
+ |s| [s.capitalize.gsub('_', ' '), s]}, {},
+ :id => element_id(@story, 'status') %>
+ </p>
+ <% unless @iterations.empty? -%>
+ <p>
+ <%= f.label :iteration_id, "Iteration" %>
+ <%= f.select :iteration_id, @iterations.map{|i|[i.name,i.id]},
+ {:prompt => "Assign Iteration"}, :id => element_id(@story,'iterations') %>
+ </p>
+ <% end -%>
+ <% end -%>
</div>
+ <p>
+ <%= in_place_editor_field 'story', 'description' %>
+ </p>
+
+ <p>
+ <%= link_to_remote "Scenarios &raquo;", :method => 'GET',
+ :url => iteration_story_scenarios_path(@iteration, @story) %>
+ </p>
+ </div>
+</div>
<% content_for :page_end do -%>
<script type="text/javascript">
-document.observe("dom:loaded", function() {
- $("<%= element_id(@story, 'points') %>").observe("change", Util.Form.selectChange);
- <% unless @iterations.empty? %>
- $("<%= element_id(@story, 'iterations') %>").observe("change", Util.Form.selectChange);
- <% end %>
- $("<%= element_id(@story, 'status') %>").observe("change", Util.Form.selectChange);
-});
+ document.observe("dom:loaded", function() {
+ $("<%= element_id(@story, 'points') %>").observe("change", Util.Form.selectChange);
+ <% unless @iterations.empty? %>
+ $("<%= element_id(@story, 'iterations') %>").observe("change", Util.Form.selectChange);
+ <% end %>
+ $("<%= element_id(@story, 'status') %>").observe("change", Util.Form.selectChange);
+ });
</script>
<% end -%>
View
18 app/views/stories/edit.html.erb
@@ -1,14 +1,14 @@
-<h1>Editing story</h1>
+<h2>Editing story</h2>
-<% form_for @story, :url => iteration_story_path(@iteration, @story) do |f| %>
- <%= f.error_messages %>
+<div id="actions">
+ <ul>
+ <li><%= link_to 'Back', iteration_stories_path(@iteration) %></li>
+ <li><%= link_to 'Show', iteration_story_path(@iteration, @story) %></li>
+ </ul>
+</div>
+<% form_for @story, :url => iteration_story_path(@iteration, @story) do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
-
- <p class="form-navigation">
- <%= link_to 'Show', iteration_story_path(@iteration, @story) %>
- <%= link_to 'Back', iteration_stories_path(@iteration), :class => "back" %>
- <%= f.submit 'Update' %>
- </p>
+ <p><%= f.submit 'Update' %></p>
<% end %>
View
115 app/views/stories/index.html.erb
@@ -1,9 +1,8 @@
-<div class="index-head">
- <h2>Iteration #<%=@iteration.name%> Stories,
- <%=@total_assigned_points%>/<%=@iteration.velocity%> points assigned
- (<%=((@total_assigned_points.to_f/@iteration.velocity.to_f)*100).to_i%>%)
- </h2>
- <p>
+<h2>Stories for <%=@iteration.name%></h2>
+
+
+<p>
+ <%=@total_assigned_points%>/<%=@iteration.velocity%> points assigned (<%=((@total_assigned_points.to_f/@iteration.velocity.to_f)*100).to_i%>%) -
<% case (@assignment_difference <=> 0)
when -1 -%>
Undersubscribed by <%= 0 - @assignment_difference%> points
@@ -12,62 +11,46 @@
<% when 0 -%>
Spot on!
<% end -%>
- </p>
- <%= link_to 'New story', new_iteration_story_path(@iteration), :class => "add" %>
-</div>
+</p>
+
+<p><%= link_to 'New story', new_iteration_story_path(@iteration), :class => "add" %></p>
<% content_for :page_end do %>
<script type="text/javascript">
-document.observe("dom:loaded", function() {
- // Extend Accordion class to force some dynamic DOM elements to appear.
- Accordion.addMethods({
- forceExpand : function() {
- var scenarios = $('.scenarios');
- if (scenarios && scenarios.length > 0)
- new Effect.Scale(scenarios[0], 100, {scaleY:true,scaleX:false,scaleMode:'contents'});
- }
- });
- <% unless @current_stories.nil? or @current_stories.empty? -%>
- var currentStories = new Accordion("current_stories", 1);
- //$('accordion-toggle').each(function(e) {e.observe('click', currentStories.forceExpand);});
- <% end -%>
- <% unless @backlog_stories.nil? or @backlog_stories.empty? -%>
- var backlogStories = new Accordion("backlog_stories", 1);
- //$('accordion-toggle').each(function(e) {e.observe('click', backlogStories.forceExpand);});
- <% end -%>
- <% unless @completed_stories.nil? or @completed_stories.empty? -%>
- var completedStories = new Accordion("completed_stories", 1);
- //$('accordion-toggle').each(function(e) {e.observe('click', completedStories.forceExpand);});
- <% end -%>
- <% unless @quality_assurance_stories.nil? or @quality_assurance_stories.empty? -%>
- var qualityAssuranceStories = new Accordion("quality_assurance_stories", 1);
- //$('accordion-toggle').each(function(e) {e.observe('click', qualityAssurance.forceExpand);});
- <% end -%>
-});
+// document.observe("dom:loaded", function() {
+// // Extend Accordion class to force some dynamic DOM elements to appear.
+// Accordion.addMethods({
+// forceExpand : function() {
+// var scenarios = $('.scenarios');
+// if (scenarios && scenarios.length > 0)
+// new Effect.Scale(scenarios[0], 100, {scaleY:true,scaleX:false,scaleMode:'contents'});
+// }
+// });
+// <% unless @current_stories.nil? or @current_stories.empty? -%>
+// var currentStories = new Accordion("current_stories", 1);
+// //$('accordion-toggle').each(function(e) {e.observe('click', currentStories.forceExpand);});
+// <% end -%>
+// <% unless @backlog_stories.nil? or @backlog_stories.empty? -%>
+// var backlogStories = new Accordion("backlog_stories", 1);
+// //$('accordion-toggle').each(function(e) {e.observe('click', backlogStories.forceExpand);});
+// <% end -%>
+// <% unless @completed_stories.nil? or @completed_stories.empty? -%>
+// var completedStories = new Accordion("completed_stories", 1);
+// //$('accordion-toggle').each(function(e) {e.observe('click', completedStories.forceExpand);});
+// <% end -%>
+// <% unless @quality_assurance_stories.nil? or @quality_assurance_stories.empty? -%>
+// var qualityAssuranceStories = new Accordion("quality_assurance_stories", 1);
+// //$('accordion-toggle').each(function(e) {e.observe('click', qualityAssurance.forceExpand);});
+// <% end -%>
+// });
</script>
<% end %>
-<div id="current-wrapper">
-<h3>Current Stories <%=points_label(@current_stories)%></h3>
- <div id="current_stories" class="accordion">
- <% @current_stories.each do |story| %>
- <%= render :partial => "story", :locals => { :story => story } %>
- <% end %>
- </div>
-
- <h3>Quality Assurance Stories <%=points_label(@quality_assurance_stories)%></h3>
- <div id="quality_assurance_stories" class="accordion">
- <% @quality_assurance_stories.each do |story| %>
- <%= render :partial => "story", :locals => { :story => story } %>
- <% end %>
- </div>
-</div>
-
<div id="backlog-wrapper">
-<h3>Story Backlog <%=points_label(@backlog_stories)%></h3>
- <div id="backlog_stories" class="accordion">
+<h3>New stories <%=points_label(@backlog_stories)%></h3>
+ <div id="backlog_stories" class="story-holder">
<% if @backlog_stories.empty? %>
- <%=link_to "Write some more!", new_iteration_story_path(@iteration) %>
+ <p>There are currently no stories for this iteration, <%=link_to "add story", new_iteration_story_path(@iteration) %></p>
<% else %>
<% @backlog_stories.each do |story| %>
<%= render :partial => "story", :locals => { :story => story } %>
@@ -76,19 +59,27 @@ document.observe("dom:loaded", function() {
</div>
</div>
-<% unless @completed_stories.empty? %>
+<div id="current-wrapper">
+<h3>In progress stories <%=points_label(@current_stories)%></h3>
+ <div id="current_stories" class="story-holder">
+ <% @current_stories.each do |story| %>
+ <%= render :partial => "story", :locals => { :story => story } %>
+ <% end %>
+ </div>
-<div style="clear:both">
+ <h3>Quality assurance stories <%=points_label(@quality_assurance_stories)%></h3>
+ <div id="quality_assurance_stories" class="story-holder">
+ <% @quality_assurance_stories.each do |story| %>
+ <%= render :partial => "story", :locals => { :story => story } %>
+ <% end %>
+ </div>
</div>
<div id="current-wrapper">
- <h3>Completed Stories <%=points_label(@completed_stories)%></h3>
- <div id="completed_stories" class="accordion">
+ <h3>Completed stories <%=points_label(@completed_stories)%></h3>
+ <div id="completed_stories" class="story-holder">
<% @completed_stories.each do |story| %>
<%= render :partial => "story", :locals => { :story => story } %>
<% end %>
</div>
-</div>
-
-<% end %>
-
+</div>
View
14 app/views/stories/new.html.erb
@@ -1,13 +1,13 @@
<h2>New story</h2>
-<% form_for @story, :url => iteration_stories_path(@iteration) do |f| %>
- <%= f.error_messages %>
+<div id="actions">
+ <ul>
+ <li><%= link_to 'Back', iteration_stories_path(@iteration) %></li>
+ </ul>
+</div>
+<% form_for @story, :url => iteration_stories_path(@iteration) do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
-
- <p class="form-navigation">
- <%= link_to 'Back', iteration_stories_path(@iteration), :class => "back" %>
- <%= f.submit 'Create' %>
- </p>
+ <p><%= f.submit 'Create' %></p>
<% end %>
View
9 db/migrate/20110811104522_change_title_column_length.rb
@@ -0,0 +1,9 @@
+class ChangeTitleColumnLength < ActiveRecord::Migration
+ def self.up
+ change_column :stories, :title, :string, :limit => 255
+ end
+
+ def self.down
+ change_column :stories, :title, :string, :limit => 40
+ end
+end
View
12 db/migrate/20110811110043_regenerate_story_slugs_for_broken_stories.rb
@@ -0,0 +1,12 @@
+class RegenerateStorySlugsForBrokenStories < ActiveRecord::Migration
+ def self.up
+ stories = Story.all
+ stories.each do |story|
+ story.title = story.title + "*"
+ story.save!
+ end
+ end
+
+ def self.down
+ end
+end
View
BIN  public/images/icons/delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  public/images/rails.png
Deleted file not rendered
View
8 public/javascripts/application.js
@@ -34,5 +34,11 @@ var Util = {
} catch(e) {
alert("numericOnly exception: " + e);
}
- }
+ },
+
+ FadeFlash: function(){
+ window.setTimeout(function() {
+ Effect.SlideUp('flash-notice');
+ }, 5000);
+ }
}
View
11 public/stylesheets/branston.css
@@ -66,4 +66,13 @@ hr{margin:0 0 15px;}
/* #content form */
#content form{}
#content form p{overflow:hidden;}
-#content form p label{width:150px;padding:2px 0 0 ;float:left;}
+#content form p label{width:150px;padding:1px 0 0 ;float:left;}
+
+/* .story-holder */
+.story-holder{margin:0 -15px 0 0;padding:0 0 15px;overflow:hidden;}
+
+/* .story */
+.story{width:450px;margin:0 15px 15px 0;padding:10px;position:relative;float:left;border:1px solid #000;}
+.story h3{width:390px;}
+.story .delete{width:26px;height:26px;position:absolute;top:10px;right:10px;display:block;overflow:hidden;text-indent:-1000px;background:url("/images/icons/delete.png") no-repeat;}
+#content .story form p label{width:70px;}
View
16 test/functional/stories_controller_test.rb
@@ -70,7 +70,7 @@ class StoriesControllerTest < ActionController::TestCase
should "generate the cucumber feature file for a story" do
get :generate_feature, :id => @story.to_param, :path => 'test/features/',
- :iteration_id => @iteration.to_param
+ :iteration_id => @iteration.to_param
assert_response :success
assert File.exists? FEATURE_PATH + @story.feature_filename
assert File.exists? FEATURE_PATH + @story.step_filename
@@ -124,15 +124,19 @@ class StoriesControllerTest < ActionController::TestCase
end
end
- context "updating a story" do
+ context "updating a story" do
context "with valid parameters" do
setup do
assert_no_difference("Story.count") do
put :update,{ :id => @story.slug, :story => {:description => "bar"},
- :iteration_id => @iteration.to_param }
+ :iteration_id => @iteration.to_param }
end
end
+ should "leave the story as new" do
+ assert @story.new?
+ end
+
should "redirect to show" do
assert_redirected_to iteration_story_path(@iteration, @story)
end
@@ -140,8 +144,8 @@ class StoriesControllerTest < ActionController::TestCase
context "with story status set to 'in_progress'" do
setup do
put :update,{ :id => @story.to_param, :story => {
- :description => "bar", :status => "in_progress"},
- :iteration_id => @iteration.to_param }
+ :description => "bar", :status => "in_progress"
+ }, :iteration_id => @iteration.to_param }
end
should "set the story's status to 'in_progress'" do
@@ -232,6 +236,7 @@ class StoriesControllerTest < ActionController::TestCase
:id => 'none-such-story', :iteration_id => @iteration.to_param,
:username => @user.login, :password => "monkey"
end
+
should "fail gracefully" do
assert_response 404
end
@@ -242,6 +247,7 @@ class StoriesControllerTest < ActionController::TestCase
get :show,
:id => @story.to_param, :iteration_id => @iteration.to_param
end
+
should "fail gracefully" do
get :show, :id => 'none-such-story', :iteration_id => @iteration.to_param
assert_response 404
View
8 test/unit/story_test.rb
@@ -32,6 +32,7 @@ class StoryTest < ActiveSupport::TestCase
@story.finish
end
end
+
should "leave the story in a :new state" do
assert @story.new?
end
@@ -43,6 +44,7 @@ class StoryTest < ActiveSupport::TestCase
@story.check_quality
end
end
+
should "leave the story in a :new state" do
assert @story.new?
end
@@ -81,6 +83,12 @@ class StoryTest < ActiveSupport::TestCase
assert_equal 'updated-title', @story.slug
end
+ should "match to_param and slug when the title is long" do
+ @story.title = "this is a really really really really long title"
+ assert @story.save
+ assert_equal @story.to_param, @story.slug
+ end
+
should "have a unique title" do
assert_no_difference 'Story.count' do
assert_raise ActiveRecord::RecordInvalid do
Please sign in to comment.
Something went wrong with that request. Please try again.