trajectory 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f0df4ea6f99eb9dace7117580eb27e3b683641d
4
- data.tar.gz: 0ce7b25d9dd4aef130c0d193313c3f0e63258565
3
+ metadata.gz: d010f9c28f42b607cbc7d7ffd045438cad5077b1
4
+ data.tar.gz: 388c7eb91ab985f2f9d74da3ef6af81d29ecabda
5
5
  SHA512:
6
- metadata.gz: 7e838a4f8139b58a9efc800cb0ffa8b83541de05424b7bb47a8a36c71cc142225fa484e582def9d4155035eca5c92f33ad6f9c70ab0a8e3a16ec1c90e0155a61
7
- data.tar.gz: 266263a8df3e23fc6ba720ab597539a09eae9a4a207fea0c64bd64b9d8b5b0df4bbc7566c5c89face5291ca6b961f6c046494363099a278f9c5e749ca08c49c7
6
+ metadata.gz: 9d704ed03e2ef065234de2ba7b63db0b3f4554d387f5b51fa8a98784418cab1bbb33a93d40363a345e3b6bc08685395153a596eabc4394de43b10b80cccaaf29
7
+ data.tar.gz: 8bc370caa554f3e9d7fd46bec8ca0da5f5348ffcdfdef8da3f6ba1ad66726595d0b127f350bbfa1848ec72e1dafe84691c09ac0d97b897f140cadc15ad50e590
data/README.md CHANGED
@@ -1,20 +1,24 @@
1
- # Trajectory Ruby Wrapper
1
+ # Trajectory API Ruby Wrapper
2
2
 
3
3
  [![Build Status](https://travis-ci.org/lminaudier/trajectory.png?branch=master)](https://travis-ci.org/lminaudier/trajectory)
4
4
  [![Code Climate](https://codeclimate.com/github/lminaudier/trajectory.png)](https://codeclimate.com/github/lminaudier/trajectory)
5
+ [![Dependency Status](https://gemnasium.com/lminaudier/trajectory.png)](https://gemnasium.com/lminaudier/trajectory)
5
6
 
6
- This gem is a wrapper to the Thoughbot's Trajectory app ([apptrajectory.com](http://apptrajectory.com)).
7
+ This gem is a wrapper to the Thoughbot's Trajectory app API ([apptrajectory.com](http://apptrajectory.com)).
7
8
 
8
- This is **work in progress** but you'll have read access to
9
- - projects
10
- - stories
11
- - iterations
12
- - ideas
13
- - updates
9
+ This is **work in progress** but at the moment, you'll have read access to
10
+ - [Project](http://rdoc.info/gems/trajectory/Trajectory/Project)
11
+ - [Story](http://rdoc.info/gems/trajectory/Trajectory/Story)
12
+ - [Iteration](http://rdoc.info/gems/trajectory/Trajectory/Iteration)
13
+ - [Idea](http://rdoc.info/gems/trajectory/Trajectory/Idea)
14
+ - [Update](http://rdoc.info/gems/trajectory/Trajectory/Update)
14
15
 
15
- Comments, Uploads wrappers are missing.
16
+ The wrapper propose some handy methods on collections of [projects](http://rdoc.info/gems/trajectory/Trajectory/Projects), [stories](http://rdoc.info/gems/trajectory/Trajectory/Stories), [iterations](http://rdoc.info/gems/trajectory/Trajectory/Iterations) and [ideas](http://rdoc.info/gems/trajectory/Trajectory/Ideas).
16
17
 
17
- No write wrapper is provided at the moment.
18
+ Comments and Uploads wrappers are not yet implemented.
19
+ Wrapper for write access is not provided at the moment.
20
+
21
+ If you have questions, you can submit an issue or ping me on twitter [@lminaudier](https://twitter.com/lminaudier).
18
22
 
19
23
  ## Installation
20
24
 
@@ -37,8 +41,7 @@ Or install it yourself as:
37
41
  #### Authentication
38
42
 
39
43
  By default, the wrapper will look for the Trajectory API key in
40
- `TRAJECTORY_API_KEY` environment variable. You can also pass it directly to the
41
- constructor.
44
+ `TRAJECTORY_API_KEY` environment variable and for the account keyword in `TRAJECTORY_ACCOUNT_KEYWORD`.
42
45
 
43
46
  require 'trajectory'
44
47
 
@@ -47,7 +50,7 @@ constructor.
47
50
 
48
51
  #### API
49
52
 
50
- See [RDoc](http://rdoc.info/github/lminaudier/trajectory/master/frames) for api details
53
+ See [RDoc](http://rdoc.info/gems/trajectory/frames) for api details
51
54
 
52
55
  ## Contributing
53
56
 
@@ -66,5 +66,14 @@ module Trajectory
66
66
  def iterations_for_project(project)
67
67
  Iterations.from_json project, Api.iterations_for_project(project)
68
68
  end
69
+
70
+ # Fetches a iteration of a given project by id
71
+ #
72
+ # @param project [Project] the project
73
+ # @param iteration_id [Integer] the iteration id
74
+ # @return [Iteration, false] the found iteration or false
75
+ def find_iteration_of_project_with_id(project, iteration_id)
76
+ project.find_iteration_by_id(iteration_id)
77
+ end
69
78
  end
70
79
  end
@@ -22,6 +22,15 @@ module Trajectory
22
22
  end)
23
23
  end
24
24
 
25
+ # Fetch the iteration with the given id in the collection. If it is not found,
26
+ # it returns false
27
+ #
28
+ # @param id [Integer] the project id
29
+ # @return [Iteration, false] the found iteration or false
30
+ def find_by_id(id)
31
+ iterations.find { |iteration| iteration.id == id } || false
32
+ end
33
+
25
34
  # Returns the current iteration of the project or false it no current iteration can be found
26
35
  #
27
36
  # @return [Iteration, false] the current iteration or false
@@ -4,7 +4,8 @@ module Trajectory
4
4
 
5
5
  NUMBER_OF_WORKING_DAYS_BY_WEEK = 5.0
6
6
 
7
- attr_writer :stories, :users_collection
7
+ # @private
8
+ attr_writer :stories, :users_collection, :iterations_collection
8
9
 
9
10
  # @return [Integer] the unique identifier of the project.
10
11
  # @raise [MissingAttributeError] if id is nil
@@ -50,7 +51,7 @@ module Trajectory
50
51
  #
51
52
  # @return [Iterations] the iterations collection
52
53
  def iterations
53
- @iterations ||= DataStore.iterations_for_project(self)
54
+ @iterations_collection ||= DataStore.iterations_for_project(self)
54
55
  end
55
56
 
56
57
  # Fetch all ideas that belongs to the project
@@ -82,6 +83,13 @@ module Trajectory
82
83
  users.find_by_id(id)
83
84
  end
84
85
 
86
+ # Fetch a iteration from the project given its id or false if it does not exist
87
+ #
88
+ # @return [Iteration, false] the iteration or false
89
+ def find_iteration_by_id(id)
90
+ iterations.find_by_id(id)
91
+ end
92
+
85
93
  # Fetch the stories in a given iteration of a project
86
94
  #
87
95
  # @param iteration [Iteration] the iteration
@@ -106,6 +114,13 @@ module Trajectory
106
114
  Date.today + remaining_days
107
115
  end
108
116
 
117
+ # Returns the number of needed iterations to complete the project
118
+ #
119
+ # @return [Integer] the number of iterations
120
+ def remaining_iterations
121
+ (remaining_points.to_f / estimated_velocity.to_f).ceil
122
+ end
123
+
109
124
  # Returns the estimated number of days (weekend included) remaining before the end of the project.
110
125
  #
111
126
  # This is usefull to estimate the project end date.
@@ -164,5 +179,29 @@ module Trajectory
164
179
  def accepted_points
165
180
  total_points - remaining_points
166
181
  end
182
+
183
+ # Returns the last non null velocity of the project or raise an error if the
184
+ # project never started
185
+ #
186
+ # @return [Integer] a non null velocity
187
+ # @raise [VelocityAlwaysEqualToZero] when historic velocity was always zero
188
+ # (i.e the project has not yet started)
189
+ def last_non_null_velocity
190
+ raise VelocityAlwaysEqualToZero if !has_started?
191
+ historic_velocity.reverse.find do |velocity|
192
+ velocity != 0
193
+ end
194
+ end
195
+
196
+ # Returns true if the project has already started some development (i.e
197
+ # stories have been accepted and a velocity has been evaluated). It returns
198
+ # false otherwise
199
+ #
200
+ # @return [true, false]
201
+ def has_started?
202
+ historic_velocity.any? do |velocity|
203
+ velocity != 0
204
+ end
205
+ end
167
206
  end
168
207
  end
@@ -111,8 +111,12 @@ module Trajectory
111
111
  iteration_id == iteration.id
112
112
  end
113
113
 
114
- # @todo Not Yet Implemented
114
+ # Returns the iteration the story belongs to or false if iteration can't be
115
+ # found
116
+ #
117
+ # @return [Iteration, false] the iteration or false
115
118
  def iteration
119
+ @iteration ||= DataStore.find_iteration_of_project_by_id(project, iteration_id)
116
120
  end
117
121
  end
118
122
  end
@@ -1,3 +1,4 @@
1
1
  require 'trajectory/exceptions/missing_attribute_error'
2
2
  require 'trajectory/exceptions/velocity_equal_to_zero_error'
3
+ require 'trajectory/exceptions/velocity_always_equal_to_zero_error'
3
4
  require 'trajectory/exceptions/bad_environment_error'
@@ -0,0 +1,4 @@
1
+ module Trajectory
2
+ class VelocityAlwaysEqualToZero < RuntimeError
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module Trajectory
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -82,5 +82,12 @@ module Trajectory
82
82
 
83
83
  DataStore.find_user_of_project_with_id(project, id)
84
84
  end
85
+
86
+ it 'delegates fetching of user of a project to the project' do
87
+ id = 12
88
+ project.should_receive(:find_iteration_by_id).with(id)
89
+
90
+ DataStore.find_iteration_of_project_with_id(project, id)
91
+ end
85
92
  end
86
93
  end
@@ -19,6 +19,24 @@ module Trajectory
19
19
  iterations[1].project_id.should == 4567
20
20
  end
21
21
 
22
+ it 'can find an iteration by id' do
23
+ iteration = double(:iteration, id: 1234)
24
+ iterations = Iterations.new(double(:iteration, id: 1),
25
+ double(:iteration, id: 2),
26
+ iteration,
27
+ double(:iteration, id: 3))
28
+
29
+ iterations.find_by_id(1234).should == iteration
30
+ end
31
+
32
+ it "returns false when it can't find a project by id" do
33
+ iterations = Iterations.new(double(:iteration, id: 1),
34
+ double(:iteration, id: 2),
35
+ double(:iteration, id: 3))
36
+
37
+ iterations.find_by_id(1234).should == false
38
+ end
39
+
22
40
  it 'can retrieve current iteration' do
23
41
  current_iteration = double(:current? => true)
24
42
  iterations = Iterations.new(double(:current? => false),
@@ -36,6 +36,15 @@ module Trajectory
36
36
  Project.new.total_points.should == 20
37
37
  end
38
38
 
39
+ it 'can evaluate remaining iterations until project completion based on the last non nul velocity' do
40
+ stories = Stories.new(double(:story, :points => 2, :completed? => false),
41
+ double(:story, :points => 10, :completed? => false),
42
+ double(:story, :points => 8, :completed? => true))
43
+ DataStore.stub(:stories_for_project).and_return(stories)
44
+
45
+ Project.new(:estimated_velocity => 2).remaining_iterations.should == 6
46
+ end
47
+
39
48
  it 'can evaluate remaining points' do
40
49
  stories = Stories.new(double(:story, :points => 2, :completed? => false),
41
50
  double(:story, :points => 10, :completed? => false),
@@ -108,6 +117,27 @@ module Trajectory
108
117
  Project.new.accepted_points.should == 19
109
118
  end
110
119
 
120
+ it 'can retrieve the last non null velocity in its history' do
121
+ Project.new(:historic_velocity => [20, 0, 2, 3, 0, 0, 0]).last_non_null_velocity.should == 3
122
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3, 0, 0, 0]).last_non_null_velocity.should == 3
123
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3]).last_non_null_velocity.should == 3
124
+ end
125
+
126
+ it 'raises an error when trying to get last non null velocity of a project that not have yet started' do
127
+ project = Project.new
128
+ project.stub(:has_started?).and_return(false)
129
+
130
+ expect do
131
+ project.last_non_null_velocity
132
+ end.to raise_error(VelocityAlwaysEqualToZero)
133
+ end
134
+
135
+ it 'can tell if a project has started based on historic velocity' do
136
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3]).should have_started
137
+ Project.new(:historic_velocity => [0, 0, 0, 0]).should_not have_started
138
+ Project.new(:historic_velocity => []).should_not have_started
139
+ end
140
+
111
141
  it 'delegates fetching of iterations of a project to the data store' do
112
142
  DataStore.should_receive(:iterations_for_project).with(project)
113
143
 
@@ -146,6 +176,15 @@ module Trajectory
146
176
  project.find_user_by_id(1234)
147
177
  end
148
178
 
179
+ it 'can get one of its iteration by id' do
180
+ iterations = double
181
+
182
+ project.iterations_collection = iterations
183
+ iterations.should_receive(:find_by_id).with(1234)
184
+
185
+ project.find_iteration_by_id(1234)
186
+ end
187
+
149
188
  context 'when estimated velocity is zero' do
150
189
  it 'cannot estimate remaining days until the project end' do
151
190
  expect do
@@ -33,6 +33,16 @@ module Trajectory
33
33
  Story.new.project
34
34
  end
35
35
 
36
+ it 'delegates iteration fetching to data store' do
37
+ project = 1234
38
+ story = Story.new(:iteration_id => 1234)
39
+ story.stub(:project).and_return(project)
40
+
41
+ DataStore.should_receive(:find_iteration_of_project_by_id).with(project, 1234)
42
+
43
+ story.iteration
44
+ end
45
+
36
46
  it 'knows when it is started' do
37
47
  Story.new(state: :started).should be_started
38
48
  Story.new(state: :unstarted).should_not be_started
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trajectory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loïc Minaudier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-16 00:00:00.000000000 Z
11
+ date: 2013-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: virtus
@@ -77,6 +77,7 @@ files:
77
77
  - lib/trajectory/exceptions.rb
78
78
  - lib/trajectory/exceptions/bad_environment_error.rb
79
79
  - lib/trajectory/exceptions/missing_attribute_error.rb
80
+ - lib/trajectory/exceptions/velocity_always_equal_to_zero_error.rb
80
81
  - lib/trajectory/exceptions/velocity_equal_to_zero_error.rb
81
82
  - lib/trajectory/version.rb
82
83
  - spec/fabricators/iteration_fabricator.rb