Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

adding deadline to story #111

Open
wants to merge 6 commits into from

5 participants

@AriT93

I have added the deadline field and updated the view to display it as a datepicker on releases. I still need to write the specs for this, I can kill the pull request and submit them together if that would be better. Sorry for the delay on this.

@malclocke

Don't think this is supposed to be here ;)

Ahh, sorry, see it is removed in next commit

@malclocke
Owner

Hi Ari,

Yes, I'm afraid it will need specs, both rspec and Jasmine. Would be good if you could put in a request spec that ensures the datepicker is visible for a release and not for other stories.

@AriT93

I should be able to get to those in there this weekend. Still figuring out jasmine.

@AriT93

Added tests for deadline. If a deadline is present check to make sure it's after the project start date validate that deadlines can only be set on a release. Also added a request spec to ensure that the deadline field is available and a datepicker for releases.

@parndt

@malclocke how's this for merging?

@AriT93 how do you feel about rebasing against current master and squashing the commits down to an easily digestible single commit for this change?

@salgadobreno

deadlines are cool :+1:

@baschtl

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 10, 2012
  1. @AriT93

    updated for issue 39

    AriT93 authored
  2. @AriT93
Commits on May 11, 2012
  1. @AriT93

    had to update story rb to handle nil deadline

    AriT93 authored
    first run at tests for jasmine,model,request
  2. @AriT93

    hadn't saved spec file

    AriT93 authored
Commits on May 13, 2012
  1. @AriT93
Commits on May 16, 2012
  1. @AriT93
This page is out of date. Refresh to see the latest.
View
1  .gitignore
@@ -5,3 +5,4 @@ log/*.log
tmp/
.rvmrc
.sass-cache/
+*~
View
4 app/assets/javascripts/models/story.js
@@ -175,7 +175,6 @@ Fulcrum.Story = Backbone.Model.extend({
position: function() {
return parseFloat(this.get('position'));
},
-
className: function() {
var className = 'story ' + this.get('story_type');
if (this.estimable() && !this.estimated()) {
@@ -203,6 +202,9 @@ Fulcrum.Story = Backbone.Model.extend({
}).join(', ');
},
+ deadline: function(){
+ return this.get('deadline');
+ },
// Returns the user that owns this Story, or undefined if no user owns
// the Story
owned_by: function() {
View
14 app/assets/javascripts/views/form_view.js
@@ -9,20 +9,28 @@ Fulcrum.FormView = Backbone.View.extend({
value = value || elem_id;
return this.make('label', {'for': elem_id}, value);
},
-
textField: function(name, extra_opts) {
var defaults = {type: "text", name: name, value: this.model.get(name)}
this.mergeAttrs(defaults, extra_opts);
var el = this.make('input', defaults);
this.bindElementToAttribute(el, name, "keyup");
return el;
- },
+ },
+ datepicker: function(name, extra_opts) {
+ var defaults = {type: "text", name: name, value: this.model.get(name)}
+ this.mergeAttrs(defaults, extra_opts);
+ var el = this.make('input', defaults);
+ $(el).datepicker({dateFormat:"yy-mm-dd",
+ onSelect: function(dateText,inst){this.val=dateText;$(this).change();}});
+ this.bindElementToAttribute(el, name);
+ return el;
+ },
hiddenField: function(name) {
var el = this.make('input', {type: "hidden", name: name, value: this.model.get(name)});
this.bindElementToAttribute(el, name);
return el;
- },
+ },
textArea: function(name) {
var el = this.make('textarea', {name: name, value: this.model.get(name)});
View
20 app/assets/javascripts/views/story_view.js
@@ -23,7 +23,6 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
this.model.bind("change:position", this.highlight);
this.model.bind("change:estimate", this.highlight);
this.model.bind("change:story_type", this.highlight);
-
this.model.bind("change:column", this.moveColumn);
this.model.bind("change:estimate", this.setClassName);
@@ -57,9 +56,16 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
"click input.estimate": "estimate",
"click #destroy": "clear",
"click #edit-description": "editDescription",
- "sortupdate": "sortUpdate"
+ "sortupdate": "sortUpdate",
+ "change select[name='story_type']" : "changeStoryType"
},
+ changeStoryType: function(ev, ui){
+ if(ev.target.value != "release"){
+ this.model.set({deadline: null});
+ }
+ this.render();
+ },
// Triggered whenever a story is dropped to a new position
sortUpdate: function(ev, ui) {
@@ -304,6 +310,16 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
})
);
+ if( this.model.get('story_type') == 'release'){
+ this.$el.append(
+ this.makeFormControl({
+ name: "deadline",
+ label: "Deadline",
+ control: this.datepicker("deadline")
+ })
+ );
+ }
+
this.$el.append(
this.makeFormControl({
name: "labels",
View
2  app/controllers/stories_controller.rb
@@ -146,7 +146,7 @@ def state_change(transition)
def filter_story_params
allowed = [
:title, :description, :estimate, :story_type, :state, :requested_by_id,
- :owned_by_id, :position, :labels
+ :owned_by_id, :position, :labels, :deadline
]
filtered = {}
params[:story].each do |key, value|
View
18 app/models/story.rb
@@ -1,7 +1,7 @@
class Story < ActiveRecord::Base
JSON_ATTRIBUTES = [
- "title", "accepted_at", "created_at", "updated_at", "description",
+ "title", "accepted_at", "deadline", "created_at", "updated_at", "description",
"project_id", "story_type", "owned_by_id", "requested_by_id", "estimate",
"state", "position", "id", "labels"
]
@@ -19,6 +19,8 @@ class Story < ActiveRecord::Base
validates :title, :presence => true
+ validate :deadline_is_after_project_start, :deadline_is_only_allowed_for_release, :allow_nil => true
+
belongs_to :requested_by, :class_name => 'User'
validates :requested_by_id, :belongs_to_project => true
@@ -140,7 +142,7 @@ def to_csv
state, # Current State
created_at, # Created at
accepted_at, # Accepted at
- nil, # Deadline
+ deadline, # Deadline
requested_by.try(:name), # Requested By
owned_by.try(:name), # Owned By
description, # Description
@@ -203,7 +205,19 @@ def notify_users
end
private
+ def deadline_is_after_project_start
+ #No need to validate if the deadline is nil
+ return if self.deadline.nil?
+ if (!self.project.start_date.nil? && self.deadline.to_date < self.project.start_date)
+ errors.add(:deadline, "deadline must be after project start")
+ end
+ end
+ def deadline_is_only_allowed_for_release
+ if self.story_type != 'release' && !self.deadline.nil?
+ errors.add(:deadline, "deadlines can only be set on for a release")
+ end
+ end
def set_accepted_at
if state_changed?
if state == 'accepted' && accepted_at == nil
View
6 db/migrate/20120510135424_add_deadline_to_story.rb
@@ -0,0 +1,6 @@
+class AddDeadlineToStory < ActiveRecord::Migration
+ def change
+ add_column :stories, :deadline, :date
+
+ end
+end
View
187 db/schema.rb
@@ -1,93 +1,94 @@
-# encoding: UTF-8
-# This file is auto-generated from the current state of the database. Instead
-# of editing this file, please use the migrations feature of Active Record to
-# incrementally modify your database, and then regenerate this schema definition.
-#
-# Note that this schema.rb definition is the authoritative source for your
-# database schema. If you need to create the application database on another
-# system, you should be using db:schema:load, not running all the migrations
-# from scratch. The latter is a flawed and unsustainable approach (the more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
-#
-# It's strongly recommended to check this file into your version control system.
-
-ActiveRecord::Schema.define(:version => 20120504152649) do
-
- create_table "changesets", :force => true do |t|
- t.integer "story_id"
- t.integer "project_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- create_table "notes", :force => true do |t|
- t.text "note"
- t.integer "user_id"
- t.integer "story_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- create_table "projects", :force => true do |t|
- t.string "name"
- t.string "point_scale", :default => "fibonacci"
- t.date "start_date"
- t.integer "iteration_start_day", :default => 1
- t.integer "iteration_length", :default => 1
- t.datetime "created_at"
- t.datetime "updated_at"
- t.integer "default_velocity", :default => 10
- end
-
- create_table "projects_users", :id => false, :force => true do |t|
- t.integer "project_id"
- t.integer "user_id"
- end
-
- create_table "stories", :force => true do |t|
- t.string "title"
- t.text "description"
- t.integer "estimate"
- t.string "story_type", :default => "feature"
- t.string "state", :default => "unstarted"
- t.date "accepted_at"
- t.integer "requested_by_id"
- t.integer "owned_by_id"
- t.integer "project_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.decimal "position"
- t.string "labels"
- end
-
- create_table "users", :force => true do |t|
- t.string "email", :default => "", :null => false
- t.string "encrypted_password", :limit => 128, :default => "", :null => false
- t.string "reset_password_token"
- t.string "remember_token"
- t.datetime "remember_created_at"
- t.integer "sign_in_count", :default => 0
- t.datetime "current_sign_in_at"
- t.datetime "last_sign_in_at"
- t.string "current_sign_in_ip"
- t.string "last_sign_in_ip"
- t.string "confirmation_token"
- t.datetime "confirmed_at"
- t.datetime "confirmation_sent_at"
- t.string "password_salt"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.string "name"
- t.string "initials"
- t.boolean "email_delivery", :default => true
- t.boolean "email_acceptance", :default => true
- t.boolean "email_rejection", :default => true
- t.datetime "reset_password_sent_at"
- t.string "locale"
- end
-
- add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true
- add_index "users", ["email"], :name => "index_users_on_email", :unique => true
- add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
-
-end
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20120510135424) do
+
+ create_table "changesets", :force => true do |t|
+ t.integer "story_id"
+ t.integer "project_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "notes", :force => true do |t|
+ t.text "note"
+ t.integer "user_id"
+ t.integer "story_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "projects", :force => true do |t|
+ t.string "name"
+ t.string "point_scale", :default => "fibonacci"
+ t.date "start_date"
+ t.integer "iteration_start_day", :default => 1
+ t.integer "iteration_length", :default => 1
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "default_velocity", :default => 10
+ end
+
+ create_table "projects_users", :id => false, :force => true do |t|
+ t.integer "project_id"
+ t.integer "user_id"
+ end
+
+ create_table "stories", :force => true do |t|
+ t.string "title"
+ t.text "description"
+ t.integer "estimate"
+ t.string "story_type", :default => "feature"
+ t.string "state", :default => "unstarted"
+ t.date "accepted_at"
+ t.integer "requested_by_id"
+ t.integer "owned_by_id"
+ t.integer "project_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.decimal "position"
+ t.string "labels"
+ t.date "deadline"
+ end
+
+ create_table "users", :force => true do |t|
+ t.string "email", :default => "", :null => false
+ t.string "encrypted_password", :limit => 128, :default => "", :null => false
+ t.string "reset_password_token"
+ t.string "remember_token"
+ t.datetime "remember_created_at"
+ t.integer "sign_in_count", :default => 0
+ t.datetime "current_sign_in_at"
+ t.datetime "last_sign_in_at"
+ t.string "current_sign_in_ip"
+ t.string "last_sign_in_ip"
+ t.string "confirmation_token"
+ t.datetime "confirmed_at"
+ t.datetime "confirmation_sent_at"
+ t.string "password_salt"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.string "name"
+ t.string "initials"
+ t.boolean "email_delivery", :default => true
+ t.boolean "email_acceptance", :default => true
+ t.boolean "email_rejection", :default => true
+ t.datetime "reset_password_sent_at"
+ t.string "locale"
+ end
+
+ add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true
+ add_index "users", ["email"], :name => "index_users_on_email", :unique => true
+ add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
+
+end
View
9 spec/javascripts/models/story_spec.js
@@ -413,4 +413,13 @@ describe('Fulcrum.Story', function() {
});
+ describe("deadline",function(){
+ it("should allow a deadline to be set on a release story",function(){
+ this.story.set({story_type: 'release'});
+ expect(this.story.get('deadline')).toBeFalsy();
+ this.story.set({deadline: '05-09-2012'});
+ expect(this.story.get('deadline')).toEqual('05-09-2012');
+ });
+ });
+
});
View
22 spec/models/story_spec.rb
@@ -37,6 +37,22 @@
subject.project_id = nil
subject.should have(1).error_on(:project_id)
end
+
+ end
+
+ describe '#deadline' do
+ it "should not allow a deadline before start_date" do
+ subject.project.start_date = '05-12-2012'
+ subject.story_type = "release"
+ subject.deadline = '05-11-2012'
+ subject.should have(1).error_on(:deadline)
+ end
+ it "should only allow a deadline on releases" do
+ subject.project.start_date = '05-12-2012'
+ subject.deadline = '06-12-2012'
+ subject.story_type = "bug"
+ subject.should have(1).error_on(:deadline)
+ end
end
describe '#estimate' do
@@ -66,7 +82,7 @@
end
describe "#estimated?" do
-
+
context "when estimate is nil" do
before { subject.estimate = nil }
it { should_not be_estimated }
@@ -112,7 +128,7 @@
subject.as_json['story'].keys.sort.should == [
"title", "accepted_at", "created_at", "updated_at", "description",
"project_id", "story_type", "owned_by_id", "requested_by_id", "estimate",
- "state", "position", "id", "errors", "labels", "notes"
+ "state", "position", "id", "errors", "labels", "notes", "deadline"
].sort
end
end
@@ -177,7 +193,7 @@
end
it "is unset when state changes from 'accepted'" do
- subject.accepted_at = Date.parse('1999/01/01')
+ subject.accepted_at = Date.parse('1999/01/01')
subject.update_attribute :state, 'accepted'
subject.update_attribute :state, 'started'
subject.accepted_at.should be_nil
View
22 spec/requests/stories_spec.rb
@@ -21,6 +21,28 @@
:users => [user]
end
+ describe "Set deadline on release" do
+
+ before do
+ project
+ end
+
+ it "creates a release story and sets a deadline", :js => true do
+ visit project_path(project)
+
+ click_on 'Add story'
+
+ within('#chilly_bin') do
+ fill_in 'title', :with => 'New story'
+ select "release", :from => 'story_type'
+ find_field('deadline')
+ select "bug", :from => 'story_type'
+ page.should_not have_selector(".hasDatepicker")
+ click_on 'Save'
+ end
+
+ end
+ end
describe "full story life cycle" do
before do
Something went wrong with that request. Please try again.